diff options
Diffstat (limited to 'include/EASTL/string.h')
-rw-r--r-- | include/EASTL/string.h | 4100 |
1 files changed, 4100 insertions, 0 deletions
diff --git a/include/EASTL/string.h b/include/EASTL/string.h new file mode 100644 index 0000000..82816a4 --- /dev/null +++ b/include/EASTL/string.h @@ -0,0 +1,4100 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// 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, +// 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. +// - basic_string internally manually expands basic functions such as begin(), +// 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 +// 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 +// 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 +// 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 +// trick of using: basic_string<char>(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 +// into the string via some external means such as C strcpy or sprintf. +// - basic_string substr() deviates from the standard and returns a string with +// a copy of this->get_allocator() +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// 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 +// (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: +// string a("hello"); +// string b(a); +// 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 +// with atomic operations or mutexes. +// +// 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 +// 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 +// on weaker memory systems such as console gaming platforms. +// - All non-const string accessor functions need to do a sharing check then 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. +// 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 +// 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 +// Here is a Usenet discussion on the topic: +// http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_thread/thread/3dc6af5198d0bf7/886c8642cb06e03d +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_STRING_H +#define EASTL_STRING_H + +#include <EASTL/internal/config.h> +#include <EASTL/allocator.h> +#include <EASTL/iterator.h> +#include <EASTL/algorithm.h> +#include <EASTL/initializer_list.h> +#include <EASTL/bonus/compressed_pair.h> + +EA_DISABLE_ALL_VC_WARNINGS() +#include <stddef.h> // size_t, ptrdiff_t, etc. +#include <stdarg.h> // vararg functionality. + +#include <stdlib.h> // malloc, free. +#include <stdio.h> // snprintf, etc. +#include <ctype.h> // toupper, etc. + +EA_DISABLE_GCC_WARNING(-Wtype-limits) +#include <wchar.h> +EA_RESTORE_GCC_WARNING() + +#include <string.h> // strlen, etc. + +#if EASTL_EXCEPTIONS_ENABLED + #include <stdexcept> // std::out_of_range, std::length_error. +#endif +EA_RESTORE_ALL_VC_WARNINGS() + + +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4480 - nonstandard extension used: specifying underlying type for enum +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +// 4267 - 'argument' : conversion from 'size_t' to 'const uint32_t', possible loss of data. This is a bogus warning resulting from a bug in VC++. +// 4702 - unreachable code +EA_DISABLE_VC_WARNING(4530 4480 4571 4267 4702); + + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + +#include <EASTL/internal/char_traits.h> +#include <EASTL/string_view.h> + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_STRING_EXPLICIT +// +// See EASTL_STRING_OPT_EXPLICIT_CTORS for documentation. +// +#if EASTL_STRING_OPT_EXPLICIT_CTORS + #define EASTL_STRING_EXPLICIT explicit +#else + #define EASTL_STRING_EXPLICIT +#endif +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// 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 +// 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 +// implementations, and may simply use C vsnprintf if desired, though it's not +// completely portable between compilers. +// +#if EASTL_EASTDC_VSNPRINTF + namespace EA + { + namespace StdC + { + // Provided by the EAStdC package or by the user. + EASTL_EASTDC_API int Vsnprintf(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, va_list arguments); + EASTL_EASTDC_API int Vsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments); + EASTL_EASTDC_API int Vsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments); + #if EA_CHAR8_UNIQUE + EASTL_EASTDC_API int Vsnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments); + #endif + #if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE + EASTL_EASTDC_API int Vsnprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments); + #endif + } + } + + namespace eastl + { + inline int Vsnprintf(char* EA_RESTRICT pDestination, size_t n, const char* EA_RESTRICT pFormat, va_list arguments) + { return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments); } + + inline int Vsnprintf(char16_t* EA_RESTRICT pDestination, size_t n, const char16_t* EA_RESTRICT pFormat, va_list arguments) + { return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments); } + + inline int Vsnprintf(char32_t* EA_RESTRICT pDestination, size_t n, const char32_t* EA_RESTRICT pFormat, va_list arguments) + { return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments); } + + #if EA_CHAR8_UNIQUE + inline int Vsnprintf(char8_t* EA_RESTRICT pDestination, size_t n, const char8_t* EA_RESTRICT pFormat, va_list arguments) + { return EA::StdC::Vsnprintf((char*)pDestination, n, (const char*)pFormat, arguments); } + #endif + + #if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE + inline int Vsnprintf(wchar_t* EA_RESTRICT pDestination, size_t n, const wchar_t* EA_RESTRICT pFormat, va_list arguments) + { return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments); } + #endif + } +#else + // User-provided functions. + extern int Vsnprintf8 (char* pDestination, size_t n, const char* pFormat, va_list arguments); + extern int Vsnprintf16(char16_t* pDestination, size_t n, const char16_t* pFormat, va_list arguments); + extern int Vsnprintf32(char32_t* pDestination, size_t n, const char32_t* pFormat, va_list arguments); + #if EA_CHAR8_UNIQUE + extern int Vsnprintf8 (char8_t* pDestination, size_t n, const char8_t* pFormat, va_list arguments); + #endif + #if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE + extern int VsnprintfW(wchar_t* pDestination, size_t n, const wchar_t* pFormat, va_list arguments); + #endif + + namespace eastl + { + inline int Vsnprintf(char* pDestination, size_t n, const char* pFormat, va_list arguments) + { return Vsnprintf8(pDestination, n, pFormat, arguments); } + + inline int Vsnprintf(char16_t* pDestination, size_t n, const char16_t* pFormat, va_list arguments) + { return Vsnprintf16(pDestination, n, pFormat, arguments); } + + inline int Vsnprintf(char32_t* pDestination, size_t n, const char32_t* pFormat, va_list arguments) + { return Vsnprintf32(pDestination, n, pFormat, arguments); } + + #if EA_CHAR8_UNIQUE + inline int Vsnprintf(char8_t* pDestination, size_t n, const char8_t* pFormat, va_list arguments) + { return Vsnprintf8(pDestination, n, pFormat, arguments); } + #endif + + #if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE + inline int Vsnprintf(wchar_t* pDestination, size_t n, const wchar_t* pFormat, va_list arguments) + { return VsnprintfW(pDestination, n, pFormat, arguments); } + #endif + } +#endif +/////////////////////////////////////////////////////////////////////////////// + + + +namespace eastl +{ + + /// EASTL_BASIC_STRING_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_BASIC_STRING_DEFAULT_NAME + #define EASTL_BASIC_STRING_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " basic_string" // Unless the user overrides something, this is "EASTL basic_string". + #endif + + + /// EASTL_BASIC_STRING_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_BASIC_STRING_DEFAULT_ALLOCATOR + #define EASTL_BASIC_STRING_DEFAULT_ALLOCATOR allocator_type(EASTL_BASIC_STRING_DEFAULT_NAME) + #endif + + + /////////////////////////////////////////////////////////////////////////////// + /// basic_string + /// + /// Implements a templated string class, somewhat like C++ std::basic_string. + /// + /// 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 + /// 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 + /// 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 + /// occur with std containers. + /// + template <typename T, typename Allocator = EASTLAllocatorType> + class basic_string + { + public: + typedef basic_string<T, Allocator> this_type; + typedef basic_string_view<T> view_type; + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T* iterator; // Maintainer note: We want to leave iterator defined as T* -- at least in release builds -- as this gives some algorithms an advantage that optimizers cannot get around. + typedef const T* const_iterator; + typedef eastl::reverse_iterator<iterator> reverse_iterator; + typedef eastl::reverse_iterator<const_iterator> const_reverse_iterator; + typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t. + 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. + + public: + // 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 + // 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 + // 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. + 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' + }; + + template <typename CharT, size_t = sizeof(CharT)> + struct SSOPadding + { + char padding[sizeof(CharT) - sizeof(char)]; + }; + + template <typename CharT> + struct SSOPadding<CharT, 1> + { + // 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 + { + static constexpr size_type SSO_CAPACITY = (sizeof(HeapLayout) - sizeof(char)) / sizeof(value_type); + + // 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<value_type> + { + char mnRemainingSize; + }; + + value_type mData[SSO_CAPACITY]; // Local buffer for string data. + SSOSize mRemainingSizeField; + }; + + // This view of memory is a utility structure for easy copying of the string data. + struct RawLayout + { + 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"); + + // 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. + struct Layout + { + union + { + HeapLayout heap; + SSOLayout sso; + RawLayout raw; + }; + + Layout() { ResetToSSO(); } // start as SSO by default + 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; } + + // 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.mRemainingSizeField.mnRemainingSize & 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; } + + // 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 + { + #ifdef EA_SYSTEM_BIG_ENDIAN + return SSOLayout::SSO_CAPACITY - (sso.mRemainingSizeField.mnRemainingSize >> 2); + #else + return (SSOLayout::SSO_CAPACITY - sso.mRemainingSizeField.mnRemainingSize); + #endif + } + inline size_type GetHeapSize() const EA_NOEXCEPT { return heap.mnSize; } + inline size_type GetSize() const EA_NOEXCEPT { return IsHeap() ? GetHeapSize() : GetSSOSize(); } + + inline void SetSSOSize(size_type size) EA_NOEXCEPT + { + #ifdef EA_SYSTEM_BIG_ENDIAN + sso.mRemainingSizeField.mnRemainingSize = (char)((SSOLayout::SSO_CAPACITY - size) << 2); + #else + sso.mRemainingSizeField.mnRemainingSize = (char)(SSOLayout::SSO_CAPACITY - size); + #endif + } + + inline void SetHeapSize(size_type size) EA_NOEXCEPT { heap.mnSize = size; } + inline void SetSize(size_type size) EA_NOEXCEPT { IsHeap() ? SetHeapSize(size) : SetSSOSize(size); } + + inline size_type GetRemainingCapacity() const EA_NOEXCEPT { return size_type(CapacityPtr() - EndPtr()); } + + inline value_type* HeapBeginPtr() EA_NOEXCEPT { return heap.mpBegin; }; + inline const value_type* HeapBeginPtr() const EA_NOEXCEPT { return heap.mpBegin; }; + + 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 value_type* HeapCapacityPtr() EA_NOEXCEPT { return heap.mpBegin + GetHeapCapacity(); } + inline const value_type* HeapCapacityPtr() const EA_NOEXCEPT { return heap.mpBegin + GetHeapCapacity(); } + + inline value_type* SSOCapacityPtr() EA_NOEXCEPT { return sso.mData + SSOLayout::SSO_CAPACITY; } + inline const value_type* SSOCapacityPtr() const EA_NOEXCEPT { return sso.mData + SSOLayout::SSO_CAPACITY; } + + // Points to end of the buffer at the terminating '0', *ptr == '0' <- only true when size() == capacity() + inline value_type* CapacityPtr() EA_NOEXCEPT { return IsHeap() ? HeapCapacityPtr() : SSOCapacityPtr(); } + inline const value_type* CapacityPtr() const EA_NOEXCEPT { return IsHeap() ? HeapCapacityPtr() : SSOCapacityPtr(); } + + inline void SetHeapBeginPtr(value_type* pBegin) EA_NOEXCEPT { heap.mpBegin = pBegin; } + + inline void SetHeapCapacity(size_type cap) EA_NOEXCEPT + { + #ifdef EA_SYSTEM_BIG_ENDIAN + heap.mnCapacity = (cap << 1) | kHeapMask; + #else + heap.mnCapacity = (cap | kHeapMask); + #endif + } + + inline size_type GetHeapCapacity() const EA_NOEXCEPT + { + #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 { *SSOBeginPtr() = 0; SetSSOSize(0); } + }; + + eastl::compressed_pair<Layout, allocator_type> mPair; + + inline Layout& internalLayout() EA_NOEXCEPT { return mPair.first(); } + inline const Layout& internalLayout() const EA_NOEXCEPT { return mPair.first(); } + inline allocator_type& internalAllocator() EA_NOEXCEPT { return mPair.second(); } + inline const allocator_type& internalAllocator() const EA_NOEXCEPT { return mPair.second(); } + + public: + // Constructor, destructor + basic_string() EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(EASTL_BASIC_STRING_DEFAULT_ALLOCATOR)); + explicit basic_string(const allocator_type& allocator) EA_NOEXCEPT; + basic_string(const this_type& x, size_type position, size_type n = npos); + basic_string(const value_type* p, size_type n, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); + 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 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, ...); + basic_string(std::initializer_list<value_type> init, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); + + basic_string(this_type&& x) EA_NOEXCEPT; + basic_string(this_type&& x, const allocator_type& allocator); + + explicit basic_string(const view_type& sv, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); + basic_string(const view_type& sv, size_type position, size_type n, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); + + template <typename OtherCharType> + basic_string(CtorConvert, const OtherCharType* p, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); + + template <typename OtherCharType> + basic_string(CtorConvert, const OtherCharType* p, size_type n, const allocator_type& allocator = EASTL_BASIC_STRING_DEFAULT_ALLOCATOR); + + template <typename OtherStringType> // Unfortunately we need the CtorConvert here because otherwise this function would collide with the value_type* constructor. + basic_string(CtorConvert, const OtherStringType& x); + + ~basic_string(); + + // Allocator + const allocator_type& get_allocator() const EA_NOEXCEPT; + allocator_type& get_allocator() EA_NOEXCEPT; + void set_allocator(const allocator_type& allocator); + + // Implicit conversion operator + operator basic_string_view<T>() const EA_NOEXCEPT; + + // Operator= + this_type& operator=(const this_type& x); + this_type& operator=(const value_type* p); + this_type& operator=(value_type c); + this_type& operator=(std::initializer_list<value_type> ilist); + this_type& operator=(view_type v); + this_type& operator=(this_type&& x); // TODO(c++17): noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); + + #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 <typename OtherCharType> + this_type& operator=(const OtherCharType* p); + + template <typename OtherStringType> + this_type& operator=(const OtherStringType& x); + #endif + + void swap(this_type& x); // TODO(c++17): noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value); + + // Assignment operations + this_type& assign(const this_type& x); + 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<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); + this_type& assign(std::initializer_list<value_type>); + + template <typename OtherCharType> + this_type& assign_convert(const OtherCharType* p); + + template <typename OtherCharType> + this_type& assign_convert(const OtherCharType* p, size_type n); + + template <typename OtherStringType> + this_type& assign_convert(const OtherStringType& x); + + // Iterators. + iterator begin() EA_NOEXCEPT; // Expanded in source code as: mpBegin + const_iterator begin() const EA_NOEXCEPT; // Expanded in source code as: mpBegin + const_iterator cbegin() const EA_NOEXCEPT; + + iterator end() EA_NOEXCEPT; // Expanded in source code as: mpEnd + const_iterator end() const EA_NOEXCEPT; // Expanded in source code as: mpEnd + const_iterator cend() const EA_NOEXCEPT; + + reverse_iterator rbegin() EA_NOEXCEPT; + const_reverse_iterator rbegin() const EA_NOEXCEPT; + const_reverse_iterator crbegin() const EA_NOEXCEPT; + + reverse_iterator rend() EA_NOEXCEPT; + const_reverse_iterator rend() const EA_NOEXCEPT; + const_reverse_iterator crend() const EA_NOEXCEPT; + + + // Size-related functionality + 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; + value_type* data() EA_NOEXCEPT; + const value_type* c_str() const EA_NOEXCEPT; + + // Element access + reference operator[](size_type n); + const_reference operator[](size_type n) const; + reference at(size_type n); + const_reference at(size_type n) const; + reference front(); + const_reference front() const; + reference back(); + const_reference back() const; + + // Append operations + this_type& operator+=(const this_type& x); + this_type& operator+=(const value_type* p); + 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 = 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); + this_type& append(const value_type* pBegin, const value_type* pEnd); + + this_type& append_sprintf_va_list(const value_type* pFormat, va_list arguments); + this_type& append_sprintf(const value_type* pFormat, ...); + + template <typename OtherCharType> + this_type& append_convert(const OtherCharType* p); + + template <typename OtherCharType> + this_type& append_convert(const OtherCharType* p, size_type n); + + template <typename OtherStringType> + this_type& append_convert(const OtherStringType& x); + + void push_back(value_type c); + void pop_back(); + + // Insertion operations + this_type& insert(size_type position, const this_type& x); + this_type& insert(size_type position, const this_type& x, size_type beg, size_type n); + this_type& insert(size_type position, const value_type* p, size_type n); + this_type& insert(size_type position, const value_type* p); + this_type& insert(size_type position, size_type n, value_type c); + iterator insert(const_iterator p, value_type c); + iterator insert(const_iterator p, size_type n, value_type c); + iterator insert(const_iterator p, const value_type* pBegin, const value_type* pEnd); + iterator insert(const_iterator p, std::initializer_list<value_type>); + + // Erase operations + this_type& erase(size_type position = 0, size_type n = npos); + iterator erase(const_iterator p); + iterator erase(const_iterator pBegin, const_iterator pEnd); + reverse_iterator erase(reverse_iterator position); + reverse_iterator erase(reverse_iterator first, reverse_iterator last); + void clear() EA_NOEXCEPT; + + // Detach memory + 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 = 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); + this_type& replace(const_iterator first, const_iterator last, const this_type& x); + this_type& replace(const_iterator first, const_iterator last, const value_type* p, size_type n); + this_type& replace(const_iterator first, const_iterator last, const value_type* p); + this_type& replace(const_iterator first, const_iterator last, size_type n, value_type c); + this_type& replace(const_iterator first, const_iterator last, const value_type* pBegin, const value_type* pEnd); + 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 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 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; + + // Find first-of operations + size_type find_first_of(const this_type& x, size_type position = 0) const EA_NOEXCEPT; + size_type find_first_of(const value_type* p, size_type position = 0) const; + size_type find_first_of(const value_type* p, size_type position, size_type n) const; + size_type find_first_of(value_type c, size_type position = 0) const EA_NOEXCEPT; + + // Find last-of operations + size_type find_last_of(const this_type& x, size_type position = npos) const EA_NOEXCEPT; + size_type find_last_of(const value_type* p, size_type position = npos) const; + size_type find_last_of(const value_type* p, size_type position, size_type n) const; + size_type find_last_of(value_type c, size_type position = npos) const EA_NOEXCEPT; + + // Find first not-of operations + size_type find_first_not_of(const this_type& x, size_type position = 0) const EA_NOEXCEPT; + size_type find_first_not_of(const value_type* p, size_type position = 0) const; + size_type find_first_not_of(const value_type* p, size_type position, size_type n) const; + size_type find_first_not_of(value_type c, size_type position = 0) const EA_NOEXCEPT; + + // Find last not-of operations + size_type find_last_not_of(const this_type& x, size_type position = npos) const EA_NOEXCEPT; + size_type find_last_not_of(const value_type* p, size_type position = npos) const; + size_type find_last_not_of(const value_type* p, size_type position, size_type n) const; + size_type find_last_not_of(value_type c, size_type position = npos) const EA_NOEXCEPT; + + // Substring functionality + this_type substr(size_type position = 0, size_type n = npos) const; + + // Comparison operations + int compare(const this_type& x) const EA_NOEXCEPT; + int compare(size_type pos1, size_type n1, const this_type& x) const; + int compare(size_type pos1, size_type n1, const this_type& x, size_type pos2, size_type n2) const; + int compare(const value_type* p) const; + int compare(size_type pos1, size_type n1, const value_type* p) const; + int compare(size_type pos1, size_type n1, const value_type* p, size_type n2) const; + static int compare(const value_type* pBegin1, const value_type* pEnd1, const value_type* pBegin2, const value_type* pEnd2); + + // Case-insensitive comparison functions. Not part of C++ this_type. Only ASCII-level locale functionality is supported. Thus this is not suitable for localization purposes. + int comparei(const this_type& x) const EA_NOEXCEPT; + int comparei(const value_type* p) const; + static int comparei(const value_type* pBegin1, const value_type* pEnd1, const value_type* pBegin2, const value_type* pEnd2); + + // Misc functionality, not part of C++ this_type. + void make_lower(); + void make_upper(); + void ltrim(); + void rtrim(); + void trim(); + void ltrim(const value_type* p); + void rtrim(const value_type* p); + void trim(const value_type* p); + this_type left(size_type n) const; + this_type right(size_type n) const; + this_type& sprintf_va_list(const value_type* pFormat, va_list arguments); + this_type& sprintf(const value_type* pFormat, ...); + + bool validate() const EA_NOEXCEPT; + int validate_iterator(const_iterator i) const EA_NOEXCEPT; + + + protected: + // Helper functions for initialization/insertion operations. + value_type* DoAllocate(size_type n); + void DoFree(value_type* p, size_type n); + size_type GetNewCapacity(size_type currentCapacity); + size_type GetNewCapacity(size_type currentCapacity, size_type minimumGrowSize); + void AllocateSelf(); + void AllocateSelf(size_type n); + void DeallocateSelf(); + iterator InsertInternal(const_iterator p, value_type c); + void RangeInitialize(const value_type* pBegin, const value_type* pEnd); + void RangeInitialize(const value_type* pBegin); + void SizeInitialize(size_type n, value_type c); + + bool IsSSO() const EA_NOEXCEPT; + + void ThrowLengthException() const; + void ThrowRangeException() const; + void ThrowInvalidArgumentException() const; + + #if EASTL_OPERATOR_EQUALS_OTHER_ENABLED + template <typename CharType> + void DoAssignConvert(CharType c, true_type); + + template <typename StringType> + void DoAssignConvert(const StringType& x, false_type); + #endif + + // Replacements for STL template functions. + static const value_type* CharTypeStringFindEnd(const value_type* pBegin, const value_type* pEnd, value_type c); + static const value_type* CharTypeStringRFind(const value_type* pRBegin, const value_type* pREnd, const value_type c); + static const value_type* CharTypeStringSearch(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); + static const value_type* CharTypeStringRSearch(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); + static const value_type* CharTypeStringFindFirstOf(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); + static const value_type* CharTypeStringRFindFirstOf(const value_type* p1RBegin, const value_type* p1REnd, const value_type* p2Begin, const value_type* p2End); + static const value_type* CharTypeStringFindFirstNotOf(const value_type* p1Begin, const value_type* p1End, const value_type* p2Begin, const value_type* p2End); + static const value_type* CharTypeStringRFindFirstNotOf(const value_type* p1RBegin, const value_type* p1REnd, const value_type* p2Begin, const value_type* p2End); + + }; // basic_string + + + + + + /////////////////////////////////////////////////////////////////////////////// + // basic_string + /////////////////////////////////////////////////////////////////////////////// + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string() EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(EASTL_BASIC_STRING_DEFAULT_ALLOCATOR)) + : mPair(allocator_type(EASTL_BASIC_STRING_DEFAULT_NAME)) + { + AllocateSelf(); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const allocator_type& allocator) EA_NOEXCEPT + : mPair(allocator) + { + AllocateSelf(); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const this_type& x) + : mPair(x.get_allocator()) + { + RangeInitialize(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>::basic_string(const this_type& x, const allocator_type& allocator) + : mPair(allocator) + { + RangeInitialize(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + template <typename OtherStringType> + inline basic_string<T, Allocator>::basic_string(CtorConvert, const OtherStringType& x) + : mPair(x.get_allocator()) + { + AllocateSelf(); + append_convert(x.c_str(), x.length()); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>::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 > x.internalLayout().GetSize())) // 21.4.2 p4 + { + ThrowRangeException(); + AllocateSelf(); + } + else + RangeInitialize( + 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, x.internalLayout().GetSize() - position)); + #endif + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const value_type* p, size_type n, const allocator_type& allocator) + : mPair(allocator) + { + RangeInitialize(p, p + n); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const view_type& sv, const allocator_type& allocator) + : basic_string(sv.data(), sv.size(), allocator) + { + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const view_type& sv, size_type position, size_type n, const allocator_type& allocator) + : basic_string(sv.substr(position, n), allocator) + { + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + inline basic_string<T, Allocator>::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 + append_convert(p); // implement this in the simplest way, by simply default-constructing and calling assign. + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + inline basic_string<T, Allocator>::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 + append_convert(p, n); // implement this in the simplest way, by simply default-constructing and calling assign. + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const value_type* p, const allocator_type& allocator) + : mPair(allocator) + { + RangeInitialize(p); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(size_type n, value_type c, const allocator_type& allocator) + : mPair(allocator) + { + SizeInitialize(n, c); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::basic_string(const value_type* pBegin, const value_type* pEnd, const allocator_type& allocator) + : mPair(allocator) + { + RangeInitialize(pBegin, pEnd); + } + + + // 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 <typename T, typename Allocator> + basic_string<T, Allocator>::basic_string(CtorDoNotInitialize /*unused*/, size_type n, const allocator_type& allocator) + : mPair(allocator) + { + // Note that we do not call SizeInitialize here. + AllocateSelf(n); + internalLayout().SetSize(0); + *internalLayout().EndPtr() = 0; + } + + + // CtorSprintf exists so that we can create a version that does a variable argument + // sprintf but also doesn't collide with any other constructor declaration. + template <typename T, typename Allocator> + basic_string<T, Allocator>::basic_string(CtorSprintf /*unused*/, const value_type* pFormat, ...) + : mPair() + { + const size_type n = (size_type)CharStrlen(pFormat); + AllocateSelf(n); + internalLayout().SetSize(0); + + va_list arguments; + va_start(arguments, pFormat); + append_sprintf_va_list(pFormat, arguments); + va_end(arguments); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>::basic_string(std::initializer_list<value_type> init, const allocator_type& allocator) + : mPair(allocator) + { + RangeInitialize(init.begin(), init.end()); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>::basic_string(this_type&& x) EA_NOEXCEPT + : mPair(x.get_allocator()) + { + internalLayout() = eastl::move(x.internalLayout()); + x.AllocateSelf(); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>::basic_string(this_type&& x, const allocator_type& allocator) + : mPair(allocator) + { + if(get_allocator() == x.get_allocator()) // If we can borrow from x... + { + internalLayout() = eastl::move(x.internalLayout()); + x.AllocateSelf(); + } + else if(x.internalLayout().BeginPtr()) + { + RangeInitialize(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + // Let x destruct its own items. + } + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>::~basic_string() + { + DeallocateSelf(); + } + + + template <typename T, typename Allocator> + inline const typename basic_string<T, Allocator>::allocator_type& + basic_string<T, Allocator>::get_allocator() const EA_NOEXCEPT + { + return internalAllocator(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::allocator_type& + basic_string<T, Allocator>::get_allocator() EA_NOEXCEPT + { + return internalAllocator(); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::set_allocator(const allocator_type& allocator) + { + get_allocator() = allocator; + } + + + template <typename T, typename Allocator> + inline const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::data() const EA_NOEXCEPT + { + return internalLayout().BeginPtr(); + } + + + template <typename T, typename Allocator> + inline const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::c_str() const EA_NOEXCEPT + { + return internalLayout().BeginPtr(); + } + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::data() EA_NOEXCEPT + { + return internalLayout().BeginPtr(); + } + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::begin() EA_NOEXCEPT + { + return internalLayout().BeginPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::end() EA_NOEXCEPT + { + return internalLayout().EndPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_iterator + basic_string<T, Allocator>::begin() const EA_NOEXCEPT + { + return internalLayout().BeginPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_iterator + basic_string<T, Allocator>::cbegin() const EA_NOEXCEPT + { + return internalLayout().BeginPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_iterator + basic_string<T, Allocator>::end() const EA_NOEXCEPT + { + return internalLayout().EndPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_iterator + basic_string<T, Allocator>::cend() const EA_NOEXCEPT + { + return internalLayout().EndPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reverse_iterator + basic_string<T, Allocator>::rbegin() EA_NOEXCEPT + { + return reverse_iterator(internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reverse_iterator + basic_string<T, Allocator>::rend() EA_NOEXCEPT + { + return reverse_iterator(internalLayout().BeginPtr()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reverse_iterator + basic_string<T, Allocator>::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reverse_iterator + basic_string<T, Allocator>::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reverse_iterator + basic_string<T, Allocator>::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(internalLayout().BeginPtr()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reverse_iterator + basic_string<T, Allocator>::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(internalLayout().BeginPtr()); + } + + + template <typename T, typename Allocator> + inline bool basic_string<T, Allocator>::empty() const EA_NOEXCEPT + { + return (internalLayout().GetSize() == 0); + } + + + template <typename T, typename Allocator> + inline bool basic_string<T, Allocator>::IsSSO() const EA_NOEXCEPT + { + return internalLayout().IsSSO(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::size() const EA_NOEXCEPT + { + return internalLayout().GetSize(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::length() const EA_NOEXCEPT + { + return internalLayout().GetSize(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::max_size() const EA_NOEXCEPT + { + return kMaxSize; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::capacity() const EA_NOEXCEPT + { + if (internalLayout().IsHeap()) + { + return internalLayout().GetHeapCapacity(); + } + return SSOLayout::SSO_CAPACITY; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reference + basic_string<T, Allocator>::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 > internalLayout().GetSize())) + EASTL_FAIL_MSG("basic_string::operator[] -- out of range"); + #endif + + return internalLayout().BeginPtr()[n]; // Sometimes done as *(mpBegin + n) + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reference + basic_string<T, Allocator>::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 > internalLayout().GetSize())) + EASTL_FAIL_MSG("basic_string::operator[] -- out of range"); + #endif + + return internalLayout().BeginPtr()[n]; // Sometimes done as *(mpBegin + n) + } + + + template <typename T, typename Allocator> + basic_string<T,Allocator>::operator basic_string_view<T>() const EA_NOEXCEPT + { + return basic_string_view<T>(data(), size()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(const this_type& x) + { + if(&x != this) + { + #if EASTL_ALLOCATOR_COPY_ENABLED + bool bSlowerPathwayRequired = (get_allocator() != x.get_allocator()); + #else + bool bSlowerPathwayRequired = false; + #endif + + if(bSlowerPathwayRequired) + { + set_capacity(0); // Must use set_capacity instead of clear because set_capacity frees our memory, unlike clear. + + #if EASTL_ALLOCATOR_COPY_ENABLED + get_allocator() = x.get_allocator(); + #endif + } + + assign(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + return *this; + } + + + #if EASTL_OPERATOR_EQUALS_OTHER_ENABLED + template <typename T, typename Allocator> + template <typename CharType> + inline void basic_string<T, Allocator>::DoAssignConvert(CharType c, true_type) + { + assign_convert(&c, 1); // Call this version of append because it will result in the encoding-converting append being used. + } + + + template <typename T, typename Allocator> + template <typename StringType> + inline void basic_string<T, Allocator>::DoAssignConvert(const StringType& x, false_type) + { + //if(&x != this) // Unnecessary because &x cannot possibly equal this. + { + #if EASTL_ALLOCATOR_COPY_ENABLED + get_allocator() = x.get_allocator(); + #endif + + assign_convert(x.c_str(), x.length()); + } + } + + + template <typename T, typename Allocator> + template <typename OtherStringType> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(const OtherStringType& x) + { + clear(); + DoAssignConvert(x, is_integral<OtherStringType>()); + return *this; + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(const OtherCharType* p) + { + return assign_convert(p); + } + #endif + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(const value_type* p) + { + return assign(p, p + CharStrlen(p)); + } + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(value_type c) + { + return assign((size_type)1, c); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(this_type&& x) + { + return assign(eastl::move(x)); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(std::initializer_list<value_type> ilist) + { + return assign(ilist.begin(), ilist.end()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::this_type& basic_string<T, Allocator>::operator=(view_type v) + { + return assign(v.data(), static_cast<this_type::size_type>(v.size())); + } + + + template <typename T, typename Allocator> + void basic_string<T, Allocator>::resize(size_type n, value_type c) + { + const size_type s = internalLayout().GetSize(); + + if(n < s) + erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); + else if(n > s) + append(n - s, c); + } + + + template <typename T, typename Allocator> + void basic_string<T, Allocator>::resize(size_type n) + { + // 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 + // string class by resizing without assigning to anything. + + const size_type s = internalLayout().GetSize(); + + if(n < s) + erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); + else if(n > s) + { + append(n - s, value_type()); + } + } + + + template <typename T, typename Allocator> + void basic_string<T, Allocator>::reserve(size_type n) + { + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(n > max_size())) + ThrowLengthException(); + #endif + + // 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 > capacity()) + set_capacity(n); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::shrink_to_fit() + { + set_capacity(internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::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 = internalLayout().GetSize(); + else if(n < internalLayout().GetSize()) + { + internalLayout().SetSize(n); + *internalLayout().EndPtr() = 0; + } + + if((n < capacity() && internalLayout().IsHeap()) || (n > capacity())) + { + // 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(); + + CharStringUninitializedCopy(pOldBegin, pOldBegin + n, internalLayout().SSOBeginPtr()); + internalLayout().SetSSOSize(n); + *internalLayout().SSOEndPtr() = 0; + + DoFree(pOldBegin, nOldCap + 1); + + return; + } + + pointer pNewBegin = DoAllocate(n + 1); // We need the + 1 to accomodate the trailing 0. + size_type nSavedSize = internalLayout().GetSize(); // save the size in case we transition from sso->heap + + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pNewBegin); + *pNewEnd = 0; + + DeallocateSelf(); + + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(n); + internalLayout().SetHeapSize(nSavedSize); + } + else + { + DeallocateSelf(); + AllocateSelf(); + } + } + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::force_size(size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(n > capacity())) + ThrowRangeException(); + #elif EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(n > capacity())) + EASTL_FAIL_MSG("basic_string::force_size -- out of range"); + #endif + + internalLayout().SetSize(n); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::clear() EA_NOEXCEPT + { + internalLayout().SetSize(0); + *internalLayout().BeginPtr() = value_type(0); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::pointer + basic_string<T, Allocator>::detach() EA_NOEXCEPT + { + // The detach function is an extension function which simply forgets the + // owned pointer. It doesn't free it but rather assumes that the user + // does. If the string is utilizing the short-string-optimization when a + // detach is requested, a copy of the string into a seperate memory + // allocation occurs and the owning pointer is given to the user who is + // responsible for freeing the memory. + + pointer pDetached = nullptr; + + if (internalLayout().IsSSO()) + { + const size_type n = internalLayout().GetSize() + 1; // +1' so that we have room for the terminating 0. + pDetached = DoAllocate(n); + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pDetached); + *pNewEnd = 0; + } + else + { + pDetached = internalLayout().BeginPtr(); + } + + AllocateSelf(); // reset to string to empty + return pDetached; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reference + basic_string<T, Allocator>::at(size_type n) const + { + #if EASTL_STRING_OPT_RANGE_ERRORS + 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 >= internalLayout().GetSize())) + EASTL_FAIL_MSG("basic_string::at -- out of range"); + #endif + + return internalLayout().BeginPtr()[n]; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reference + basic_string<T, Allocator>::at(size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + 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 >= internalLayout().GetSize())) + EASTL_FAIL_MSG("basic_string::at -- out of range"); + #endif + + return internalLayout().BeginPtr()[n]; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reference + basic_string<T, Allocator>::front() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + EASTL_FAIL_MSG("basic_string::front -- empty string"); + #else + // We allow the user to reference the trailing 0 char without asserting. + #endif + + return *internalLayout().BeginPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reference + basic_string<T, Allocator>::front() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + EASTL_FAIL_MSG("basic_string::front -- empty string"); + #else + // We allow the user to reference the trailing 0 char without asserting. + #endif + + return *internalLayout().BeginPtr(); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reference + basic_string<T, Allocator>::back() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + EASTL_FAIL_MSG("basic_string::back -- empty string"); + #else + // We allow the user to reference the trailing 0 char without asserting. + #endif + + return *(internalLayout().EndPtr() - 1); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::const_reference + basic_string<T, Allocator>::back() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) // We assert if the user references the trailing 0 char. + EASTL_FAIL_MSG("basic_string::back -- empty string"); + #else + // We allow the user to reference the trailing 0 char without asserting. + #endif + + return *(internalLayout().EndPtr() - 1); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::operator+=(const this_type& x) + { + return append(x); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::operator+=(const value_type* p) + { + return append(p); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::operator+=(value_type c) + { + push_back(c); + return *this; + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::append(const this_type& x) + { + return append(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::append(const this_type& x, size_type position, size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + 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, + x.internalLayout().BeginPtr() + position + eastl::min_alt(n, x.internalLayout().GetSize() - position)); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::append(const value_type* p, size_type n) + { + return append(p, p + n); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::append(const value_type* p) + { + return append(p, p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + basic_string<T, Allocator>& basic_string<T, Allocator>::append_convert(const OtherCharType* pOther) + { + return append_convert(pOther, (size_type)CharStrlen(pOther)); + } + + + template <typename T, typename Allocator> + template <typename OtherStringType> + basic_string<T, Allocator>& basic_string<T, Allocator>::append_convert(const OtherStringType& x) + { + return append_convert(x.c_str(), x.length()); + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + basic_string<T, Allocator>& basic_string<T, Allocator>::append_convert(const OtherCharType* pOther, size_type n) + { + // Question: What do we do in the case that we have an illegally encoded source string? + // This can happen with UTF8 strings. Do we throw an exception or do we ignore the input? + // One argument is that it's not a string class' job to handle the security aspects of a + // program and the higher level application code should be verifying UTF8 string validity, + // and thus we should do the friendly thing and ignore the invalid characters as opposed + // to making the user of this function handle exceptions that are easily forgotten. + + const size_t kBufferSize = 512; + value_type selfBuffer[kBufferSize]; // This assumes that value_type is one of char8_t, char16_t, char32_t, or wchar_t. Or more importantly, a type with a trivial constructor and destructor. + value_type* const selfBufferEnd = selfBuffer + kBufferSize; + const OtherCharType* pOtherEnd = pOther + n; + + while(pOther != pOtherEnd) + { + 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; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::append(size_type n, value_type c) + { + if (n > 0) + { + const size_type nSize = internalLayout().GetSize(); + const size_type nCapacity = capacity(); + + if((nSize + n) > nCapacity) + reserve(GetNewCapacity(nCapacity, (nSize + n) - nCapacity)); + + pointer pNewEnd = CharStringUninitializedFillN(internalLayout().EndPtr(), n, c); + *pNewEnd = 0; + internalLayout().SetSize(nSize + n); + } + + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::append(const value_type* pBegin, const value_type* pEnd) + { + if(pBegin != pEnd) + { + const size_type nOldSize = internalLayout().GetSize(); + const size_type n = (size_type)(pEnd - pBegin); + const size_type nCapacity = capacity(); + const size_type nNewSize = nOldSize + n; + + if(nNewSize > nCapacity) + { + const size_type nLength = GetNewCapacity(nCapacity, nNewSize - nCapacity); + + pointer pNewBegin = DoAllocate(nLength + 1); + + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), internalLayout().EndPtr(), pNewBegin); + pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, pNewEnd); + *pNewEnd = 0; + + DeallocateSelf(); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nNewSize); + } + else + { + pointer pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, internalLayout().EndPtr()); + *pNewEnd = 0; + internalLayout().SetSize(nNewSize); + } + } + + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::append_sprintf_va_list(const value_type* pFormat, va_list arguments) + { + // From unofficial C89 extension documentation: + // The vsnprintf returns the number of characters written into the array, + // not counting the terminating null character, or a negative value + // if count or more characters are requested to be generated. + // An error can occur while converting a value for output. + + // From the C99 standard: + // The vsnprintf function returns the number of characters that would have + // been written had n been sufficiently large, not counting the terminating + // null character, or a negative value if an encoding error occurred. + // Thus, the null-terminated output has been completely written if and only + // if the returned value is nonnegative and less than n. + + // https://www.freebsd.org/cgi/man.cgi?query=vswprintf&sektion=3&manpath=freebsd-release-ports + // https://www.freebsd.org/cgi/man.cgi?query=snprintf&manpath=SuSE+Linux/i386+11.3 + // Well its time to go on an adventure... + // C99 vsnprintf states that a buffer size of zero returns the number of characters that would + // be written to the buffer irrelevant of whether the buffer is a nullptr + // But C99 vswprintf for wchar_t changes the behaviour of the return to instead say that it + // "will fail if n or more wide characters were requested to be written", so + // calling vswprintf with a buffer size of zero always returns -1 + // unless... you are MSVC where they deviate from the std and say if the buffer is NULL + // and the size is zero it will return the number of characters written or if we are using + // EAStdC which also does the sane behaviour. + +#if !EASTL_OPENSOURCE || defined(EA_PLATFORM_MICROSOFT) + size_type nInitialSize = internalLayout().GetSize(); + int nReturnValue; + + #if EASTL_VA_COPY_ENABLED + va_list argumentsSaved; + va_copy(argumentsSaved, arguments); + #endif + + nReturnValue = eastl::Vsnprintf(nullptr, 0, pFormat, arguments); + + if (nReturnValue > 0) + { + resize(nReturnValue + nInitialSize); + + #if EASTL_VA_COPY_ENABLED + va_end(arguments); + va_copy(arguments, argumentsSaved); + #endif + + nReturnValue = eastl::Vsnprintf(internalLayout().BeginPtr() + nInitialSize, static_cast<size_t>(nReturnValue) + 1, pFormat, arguments); + } + + if (nReturnValue >= 0) + internalLayout().SetSize(nInitialSize + nReturnValue); + + #if EASTL_VA_COPY_ENABLED + // va_end for arguments will be called by the caller. + va_end(argumentsSaved); + #endif + +#else + size_type nInitialSize = internalLayout().GetSize(); + size_type nInitialRemainingCapacity = internalLayout().GetRemainingCapacity(); + int nReturnValue; + + #if EASTL_VA_COPY_ENABLED + va_list argumentsSaved; + va_copy(argumentsSaved, arguments); + #endif + + nReturnValue = eastl::Vsnprintf(internalLayout().EndPtr(), (size_t)nInitialRemainingCapacity + 1, + pFormat, arguments); + + if(nReturnValue >= (int)(nInitialRemainingCapacity + 1)) // 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); + } + else if(nReturnValue < 0) // If vsnprintf is non-C99-standard + { + // In this case we either have C89 extension behaviour or C99 behaviour. + size_type n = eastl::max_alt((size_type)(SSOLayout::SSO_CAPACITY - 1), (size_type)(nInitialSize * 2)); + + for(; (nReturnValue < 0) && (n < 1000000); n *= 2) + { + #if EASTL_VA_COPY_ENABLED + va_end(arguments); + va_copy(arguments, argumentsSaved); + #endif + resize(n); + + const size_t nCapacity = (size_t)(n - nInitialSize); + nReturnValue = eastl::Vsnprintf(internalLayout().BeginPtr() + nInitialSize, nCapacity + 1, pFormat, arguments); + + if(nReturnValue == (int)(unsigned)nCapacity) + { + resize(++n); + nReturnValue = eastl::Vsnprintf(internalLayout().BeginPtr() + nInitialSize, nCapacity + 2, pFormat, arguments); + } + } + } + + if(nReturnValue >= 0) + internalLayout().SetSize(nInitialSize + nReturnValue); + + #if EASTL_VA_COPY_ENABLED + // va_end for arguments will be called by the caller. + va_end(argumentsSaved); + #endif + +#endif // EASTL_OPENSOURCE + + return *this; + } + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::append_sprintf(const value_type* pFormat, ...) + { + va_list arguments; + va_start(arguments, pFormat); + append_sprintf_va_list(pFormat, arguments); + va_end(arguments); + + return *this; + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::push_back(value_type c) + { + append((size_type)1, c); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::pop_back() + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(internalLayout().GetSize() <= 0)) + EASTL_FAIL_MSG("basic_string::pop_back -- empty string"); + #endif + + internalLayout().EndPtr()[-1] = value_type(0); + internalLayout().SetSize(internalLayout().GetSize() - 1); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::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. + return assign(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::assign(const this_type& x, size_type position, size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + 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, x.internalLayout().GetSize() - position)); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::assign(const value_type* p, size_type n) + { + return assign(p, p + n); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::assign(const value_type* p) + { + return assign(p, p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::assign(size_type n, value_type c) + { + if(n <= internalLayout().GetSize()) + { + CharTypeAssignN(internalLayout().BeginPtr(), n, c); + erase(internalLayout().BeginPtr() + n, internalLayout().EndPtr()); + } + else + { + CharTypeAssignN(internalLayout().BeginPtr(), internalLayout().GetSize(), c); + append(n - internalLayout().GetSize(), c); + } + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::assign(const value_type* pBegin, const value_type* pEnd) + { + 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().GetSize()) * sizeof(value_type)); + append(pBegin + internalLayout().GetSize(), pEnd); + } + return *this; + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::assign(std::initializer_list<value_type> ilist) + { + return assign(ilist.begin(), ilist.end()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::assign(this_type&& x) + { + if(get_allocator() == x.get_allocator()) + { + eastl::swap(internalLayout(), x.internalLayout()); + } + else + assign(x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + + return *this; + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + basic_string<T, Allocator>& basic_string<T, Allocator>::assign_convert(const OtherCharType* p) + { + clear(); + append_convert(p); + return *this; + } + + + template <typename T, typename Allocator> + template <typename OtherCharType> + basic_string<T, Allocator>& basic_string<T, Allocator>::assign_convert(const OtherCharType* p, size_type n) + { + clear(); + append_convert(p, n); + return *this; + } + + + template <typename T, typename Allocator> + template <typename OtherStringType> + basic_string<T, Allocator>& basic_string<T, Allocator>::assign_convert(const OtherStringType& x) + { + clear(); + append_convert(x.data(), x.length()); + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::insert(size_type position, const this_type& x) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - x.internalLayout().GetSize()))) + ThrowLengthException(); + #endif + + insert(internalLayout().BeginPtr() + position, x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::insert(size_type position, const this_type& x, size_type beg, size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY((position > internalLayout().GetSize()) || (beg > x.internalLayout().GetSize()))) + ThrowRangeException(); + #endif + + size_type nLength = eastl::min_alt(n, x.internalLayout().GetSize() - beg); + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - nLength))) + ThrowLengthException(); + #endif + + insert(internalLayout().BeginPtr() + position, x.internalLayout().BeginPtr() + beg, x.internalLayout().BeginPtr() + beg + nLength); + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::insert(size_type position, const value_type* p, size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - n))) + ThrowLengthException(); + #endif + + insert(internalLayout().BeginPtr() + position, p, p + n); + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::insert(size_type position, const value_type* p) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + size_type nLength = (size_type)CharStrlen(p); + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - nLength))) + ThrowLengthException(); + #endif + + insert(internalLayout().BeginPtr() + position, p, p + nLength); + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::insert(size_type position, size_type n, value_type c) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(internalLayout().GetSize() > (max_size() - n))) + ThrowLengthException(); + #endif + + insert(internalLayout().BeginPtr() + position, n, c); + return *this; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::insert(const_iterator p, value_type c) + { + if(p == internalLayout().EndPtr()) + { + push_back(c); + return internalLayout().EndPtr() - 1; + } + return InsertInternal(p, c); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::insert(const_iterator p, size_type n, value_type c) + { + 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()))) + EASTL_FAIL_MSG("basic_string::insert -- invalid position"); + #endif + + if(n) // If there is anything to insert... + { + if(internalLayout().GetRemainingCapacity() >= n) // If we have enough capacity... + { + const size_type nElementsAfter = (size_type)(internalLayout().EndPtr() - p); + + 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().SetSize(nSavedSize + n); + memmove(const_cast<value_type*>(p) + n, p, (size_t)((nElementsAfter - n) + 1) * sizeof(value_type)); + CharTypeAssignN(const_cast<value_type*>(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().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().SetSize(nSavedSize + nElementsAfter); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + internalLayout().SetSize(nOldSize); + throw; + } + #endif + + CharTypeAssignN(const_cast<value_type*>(p), nElementsAfter + 1, c); + } + } + else + { + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + const size_type nLength = GetNewCapacity(nOldCap, (nOldSize + n) - nOldCap); + + iterator pNewBegin = DoAllocate(nLength + 1); + + iterator pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); + pNewEnd = CharStringUninitializedFillN(pNewEnd, n, c); + pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; + + DeallocateSelf(); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + n); + } + } + + return internalLayout().BeginPtr() + nPosition; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::insert(const_iterator p, const value_type* pBegin, const value_type* pEnd) + { + 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()))) + EASTL_FAIL_MSG("basic_string::insert -- invalid position"); + #endif + + const size_type n = (size_type)(pEnd - pBegin); + + if(n) + { + const bool bCapacityIsSufficient = (internalLayout().GetRemainingCapacity() >= n); + const bool bSourceIsFromSelf = ((pEnd >= internalLayout().BeginPtr()) && (pBegin <= internalLayout().EndPtr())); + + 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, get_allocator()); + 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 size_type nElementsAfter = (size_type)(internalLayout().EndPtr() - p); + + if(nElementsAfter >= n) // If there are enough characters between insert pos and 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().SetSize(nSavedSize + n); + memmove(const_cast<value_type*>(p) + n, p, (size_t)((nElementsAfter - n) + 1) * sizeof(value_type)); + memmove(const_cast<value_type*>(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); + + CharStringUninitializedCopy(pMid, pEnd, internalLayout().EndPtr() + 1); + 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().SetSize(nSavedSize + nElementsAfter); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + internalLayout().SetSize(nOldSize); + throw; + } + #endif + + CharStringUninitializedCopy(pBegin, pMid, const_cast<value_type*>(p)); + } + } + else // Else we need to reallocate to implement this. + { + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + size_type nLength; + + if(bCapacityIsSufficient) // If bCapacityIsSufficient is true, then bSourceIsFromSelf must be true. + nLength = nOldSize + n; + else + nLength = GetNewCapacity(nOldCap, (nOldSize + n) - nOldCap); + + pointer pNewBegin = DoAllocate(nLength + 1); + + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); + pNewEnd = CharStringUninitializedCopy(pBegin, pEnd, pNewEnd); + pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; + + DeallocateSelf(); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + n); + } + } + + return internalLayout().BeginPtr() + nPosition; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::insert(const_iterator p, std::initializer_list<value_type> ilist) + { + return insert(p, ilist.begin(), ilist.end()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::erase(size_type position, size_type n) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + #if EASTL_ASSERT_ENABLED + 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, internalLayout().GetSize() - position)); + + return *this; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::erase(const_iterator p) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY((p < internalLayout().BeginPtr()) || (p >= internalLayout().EndPtr()))) + EASTL_FAIL_MSG("basic_string::erase -- invalid position"); + #endif + + memmove(const_cast<value_type*>(p), p + 1, (size_t)(internalLayout().EndPtr() - p) * sizeof(value_type)); + internalLayout().SetSize(internalLayout().GetSize() - 1); + return const_cast<value_type*>(p); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::erase(const_iterator pBegin, const_iterator pEnd) + { + #if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY((pBegin < internalLayout().BeginPtr()) || (pBegin > internalLayout().EndPtr()) || + (pEnd < internalLayout().BeginPtr()) || (pEnd > internalLayout().EndPtr()) || (pEnd < pBegin))) + EASTL_FAIL_MSG("basic_string::erase -- invalid position"); + #endif + + if(pBegin != pEnd) + { + memmove(const_cast<value_type*>(pBegin), pEnd, (size_t)((internalLayout().EndPtr() - pEnd) + 1) * sizeof(value_type)); + const size_type n = (size_type)(pEnd - pBegin); + internalLayout().SetSize(internalLayout().GetSize() - n); + } + return const_cast<value_type*>(pBegin); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::reverse_iterator + basic_string<T, Allocator>::erase(reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::reverse_iterator + basic_string<T, Allocator>::erase(reverse_iterator first, reverse_iterator last) + { + return reverse_iterator(erase((++last).base(), (++first).base())); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::replace(size_type position, size_type n, const this_type& x) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + const size_type nLength = eastl::min_alt(n, internalLayout().GetSize() - position); + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY((internalLayout().GetSize() - nLength) >= (max_size() - x.internalLayout().GetSize()))) + ThrowLengthException(); + #endif + + return replace(internalLayout().BeginPtr() + position, internalLayout().BeginPtr() + position + nLength, x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::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 > internalLayout().GetSize()) || (pos2 > x.internalLayout().GetSize()))) + ThrowRangeException(); + #endif + + 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((internalLayout().GetSize() - nLength1) >= (max_size() - nLength2))) + ThrowLengthException(); + #endif + + return replace(internalLayout().BeginPtr() + pos1, internalLayout().BeginPtr() + pos1 + nLength1, x.internalLayout().BeginPtr() + pos2, x.internalLayout().BeginPtr() + pos2 + nLength2); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::replace(size_type position, size_type n1, const value_type* p, size_type n2) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + const size_type nLength = eastl::min_alt(n1, internalLayout().GetSize() - position); + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY((n2 > max_size()) || ((internalLayout().GetSize() - nLength) >= (max_size() - n2)))) + ThrowLengthException(); + #endif + + return replace(internalLayout().BeginPtr() + position, internalLayout().BeginPtr() + position + nLength, p, p + n2); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::replace(size_type position, size_type n1, const value_type* p) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + 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 > max_size()) || ((internalLayout().GetSize() - nLength) >= (max_size() - n2)))) + ThrowLengthException(); + #endif + + return replace(internalLayout().BeginPtr() + position, internalLayout().BeginPtr() + position + nLength, p, p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::replace(size_type position, size_type n1, size_type n2, value_type c) + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + const size_type nLength = eastl::min_alt(n1, internalLayout().GetSize() - position); + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY((n2 > max_size()) || (internalLayout().GetSize() - nLength) >= (max_size() - n2))) + ThrowLengthException(); + #endif + + return replace(internalLayout().BeginPtr() + position, internalLayout().BeginPtr() + position + nLength, n2, c); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::replace(const_iterator pBegin, const_iterator pEnd, const this_type& x) + { + return replace(pBegin, pEnd, x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::replace(const_iterator pBegin, const_iterator pEnd, const value_type* p, size_type n) + { + return replace(pBegin, pEnd, p, p + n); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::replace(const_iterator pBegin, const_iterator pEnd, const value_type* p) + { + return replace(pBegin, pEnd, p, p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::replace(const_iterator pBegin, const_iterator pEnd, size_type n, value_type c) + { + #if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY((pBegin < internalLayout().BeginPtr()) || (pBegin > internalLayout().EndPtr()) || + (pEnd < internalLayout().BeginPtr()) || (pEnd > internalLayout().EndPtr()) || (pEnd < pBegin))) + EASTL_FAIL_MSG("basic_string::replace -- invalid position"); + #endif + + const size_type nLength = static_cast<size_type>(pEnd - pBegin); + + if(nLength >= n) + { + CharTypeAssignN(const_cast<value_type*>(pBegin), n, c); + erase(pBegin + n, pEnd); + } + else + { + CharTypeAssignN(const_cast<value_type*>(pBegin), nLength, c); + insert(pEnd, n - nLength, c); + } + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::replace(const_iterator pBegin1, const_iterator pEnd1, const value_type* pBegin2, const value_type* pEnd2) + { + #if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY((pBegin1 < internalLayout().BeginPtr()) || (pBegin1 > internalLayout().EndPtr()) || + (pEnd1 < internalLayout().BeginPtr()) || (pEnd1 > internalLayout().EndPtr()) || (pEnd1 < pBegin1))) + EASTL_FAIL_MSG("basic_string::replace -- invalid position"); + #endif + + const size_type nLength1 = (size_type)(pEnd1 - pBegin1); + const size_type nLength2 = (size_type)(pEnd2 - pBegin2); + + if(nLength1 >= nLength2) // If we have a non-expanding operation... + { + if((pBegin2 > pEnd1) || (pEnd2 <= pBegin1)) // If we have a non-overlapping operation... + memcpy(const_cast<value_type*>(pBegin1), pBegin2, (size_t)(pEnd2 - pBegin2) * sizeof(value_type)); + else + memmove(const_cast<value_type*>(pBegin1), pBegin2, (size_t)(pEnd2 - pBegin2) * sizeof(value_type)); + erase(pBegin1 + nLength2, pEnd1); + } + else // Else we are expanding. + { + if((pBegin2 > pEnd1) || (pEnd2 <= pBegin1)) // If we have a non-overlapping operation... + { + const value_type* const pMid2 = pBegin2 + nLength1; + + if((pEnd2 <= pBegin1) || (pBegin2 > pEnd1)) + memcpy(const_cast<value_type*>(pBegin1), pBegin2, (size_t)(pMid2 - pBegin2) * sizeof(value_type)); + else + memmove(const_cast<value_type*>(pBegin1), pBegin2, (size_t)(pMid2 - pBegin2) * sizeof(value_type)); + insert(pEnd1, pMid2, pEnd2); + } + 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 = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + const size_type nNewCapacity = GetNewCapacity(nOldCap, (nOldSize + (nLength2 - nLength1)) - nOldCap); + + pointer pNewBegin = DoAllocate(nNewCapacity + 1); + + pointer pNewEnd = CharStringUninitializedCopy(internalLayout().BeginPtr(), pBegin1, pNewBegin); + pNewEnd = CharStringUninitializedCopy(pBegin2, pEnd2, pNewEnd); + pNewEnd = CharStringUninitializedCopy(pEnd1, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; + + DeallocateSelf(); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nNewCapacity); + internalLayout().SetHeapSize(nOldSize + (nLength2 - nLength1)); + } + } + return *this; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::copy(value_type* p, size_type n, size_type position) const + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + // 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 <typename T, typename Allocator> + void basic_string<T, Allocator>::swap(this_type& x) + { + 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()); + } + else // else swap the contents. + { + const this_type temp(*this); // Can't call eastl::swap because that would + *this = x; // itself call this member swap function. + x = temp; + } + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find(const this_type& x, size_type position) const EA_NOEXCEPT + { + return find(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find(const value_type* p, size_type position) const + { + return find(p, position, (size_type)CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find(const value_type* p, size_type position, size_type n) const + { + // It is not clear what the requirements are for position, but since the C++ standard + // appears to be silent it is assumed for now that position can be any value. + //#if EASTL_ASSERT_ENABLED + // if(EASTL_UNLIKELY(position > (size_type)(mpEnd - mpBegin))) + // EASTL_FAIL_MSG("basic_string::find -- invalid position"); + //#endif + + 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); + + if((pTemp != internalLayout().EndPtr()) || (n == 0)) + return (size_type)(pTemp - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find(value_type c, size_type position) const EA_NOEXCEPT + { + // It is not clear what the requirements are for position, but since the C++ standard + // appears to be silent it is assumed for now that position can be any value. + //#if EASTL_ASSERT_ENABLED + // if(EASTL_UNLIKELY(position > (size_type)(mpEnd - mpBegin))) + // EASTL_FAIL_MSG("basic_string::find -- invalid position"); + //#endif + + if(EASTL_LIKELY(position < internalLayout().GetSize()))// If the position is valid... + { + const const_iterator pResult = eastl::find(internalLayout().BeginPtr() + position, internalLayout().EndPtr(), c); + + if(pResult != internalLayout().EndPtr()) + return (size_type)(pResult - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::rfind(const this_type& x, size_type position) const EA_NOEXCEPT + { + return rfind(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::rfind(const value_type* p, size_type position) const + { + return rfind(p, position, (size_type)CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::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 + // don't crash if postion is any invalid value. + //#if EASTL_ASSERT_ENABLED + // if(EASTL_UNLIKELY((position != npos) && (position > (size_type)(mpEnd - mpBegin)))) + // EASTL_FAIL_MSG("basic_string::rfind -- invalid position"); + //#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 + // never return npos, unlike the case with find. + const size_type nLength = internalLayout().GetSize(); + + if(EASTL_LIKELY(n <= nLength)) + { + if(EASTL_LIKELY(n)) + { + const const_iterator pEnd = internalLayout().BeginPtr() + eastl::min_alt(nLength - n, position) + n; + const const_iterator pResult = CharTypeStringRSearch(internalLayout().BeginPtr(), pEnd, p, p + n); + + if(pResult != pEnd) + return (size_type)(pResult - internalLayout().BeginPtr()); + } + else + return eastl::min_alt(nLength, position); + } + return npos; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::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 = internalLayout().GetSize(); + + if(EASTL_LIKELY(nLength)) + { + const value_type* const pEnd = internalLayout().BeginPtr() + eastl::min_alt(nLength - 1, position) + 1; + const value_type* const pResult = CharTypeStringRFind(pEnd, internalLayout().BeginPtr(), c); + + if(pResult != internalLayout().BeginPtr()) + return (size_type)((pResult - 1) - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_of(const this_type& x, size_type position) const EA_NOEXCEPT + { + return find_first_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_of(const value_type* p, size_type position) const + { + return find_first_of(p, position, (size_type)CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::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 < internalLayout().GetSize()))) + { + const value_type* const pBegin = internalLayout().BeginPtr() + position; + const const_iterator pResult = CharTypeStringFindFirstOf(pBegin, internalLayout().EndPtr(), p, p + n); + + if(pResult != internalLayout().EndPtr()) + return (size_type)(pResult - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_of(value_type c, size_type position) const EA_NOEXCEPT + { + return find(c, position); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_of(const this_type& x, size_type position) const EA_NOEXCEPT + { + return find_last_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_of(const value_type* p, size_type position) const + { + return find_last_of(p, position, (size_type)CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::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 = internalLayout().GetSize(); + + if(EASTL_LIKELY(nLength)) + { + const value_type* const pEnd = internalLayout().BeginPtr() + eastl::min_alt(nLength - 1, position) + 1; + const value_type* const pResult = CharTypeStringRFindFirstOf(pEnd, internalLayout().BeginPtr(), p, p + n); + + if(pResult != internalLayout().BeginPtr()) + return (size_type)((pResult - 1) - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_of(value_type c, size_type position) const EA_NOEXCEPT + { + return rfind(c, position); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_not_of(const this_type& x, size_type position) const EA_NOEXCEPT + { + return find_first_not_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_not_of(const value_type* p, size_type position) const + { + return find_first_not_of(p, position, (size_type)CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_not_of(const value_type* p, size_type position, size_type n) const + { + if(EASTL_LIKELY(position <= internalLayout().GetSize())) + { + const const_iterator pResult = + CharTypeStringFindFirstNotOf(internalLayout().BeginPtr() + position, internalLayout().EndPtr(), p, p + n); + + if(pResult != internalLayout().EndPtr()) + return (size_type)(pResult - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_first_not_of(value_type c, size_type position) const EA_NOEXCEPT + { + if(EASTL_LIKELY(position <= internalLayout().GetSize())) + { + // Todo: Possibly make a specialized version of CharTypeStringFindFirstNotOf(pBegin, pEnd, c). + const const_iterator pResult = + CharTypeStringFindFirstNotOf(internalLayout().BeginPtr() + position, internalLayout().EndPtr(), &c, &c + 1); + + if(pResult != internalLayout().EndPtr()) + return (size_type)(pResult - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_not_of(const this_type& x, size_type position) const EA_NOEXCEPT + { + return find_last_not_of(x.internalLayout().BeginPtr(), position, x.internalLayout().GetSize()); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_not_of(const value_type* p, size_type position) const + { + return find_last_not_of(p, position, (size_type)CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_not_of(const value_type* p, size_type position, size_type n) const + { + const size_type nLength = internalLayout().GetSize(); + + if(EASTL_LIKELY(nLength)) + { + const value_type* const pEnd = internalLayout().BeginPtr() + eastl::min_alt(nLength - 1, position) + 1; + const value_type* const pResult = CharTypeStringRFindFirstNotOf(pEnd, internalLayout().BeginPtr(), p, p + n); + + if(pResult != internalLayout().BeginPtr()) + return (size_type)((pResult - 1) - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::find_last_not_of(value_type c, size_type position) const EA_NOEXCEPT + { + const size_type nLength = internalLayout().GetSize(); + + if(EASTL_LIKELY(nLength)) + { + // Todo: Possibly make a specialized version of CharTypeStringRFindFirstNotOf(pBegin, pEnd, c). + const value_type* const pEnd = internalLayout().BeginPtr() + eastl::min_alt(nLength - 1, position) + 1; + const value_type* const pResult = CharTypeStringRFindFirstNotOf(pEnd, internalLayout().BeginPtr(), &c, &c + 1); + + if(pResult != internalLayout().BeginPtr()) + return (size_type)((pResult - 1) - internalLayout().BeginPtr()); + } + return npos; + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator> basic_string<T, Allocator>::substr(size_type position, size_type n) const + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(position > internalLayout().GetSize())) + ThrowRangeException(); + #elif EASTL_ASSERT_ENABLED + 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, internalLayout().GetSize() - position), get_allocator()); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::compare(const this_type& x) const EA_NOEXCEPT + { + return compare(internalLayout().BeginPtr(), internalLayout().EndPtr(), x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::compare(size_type pos1, size_type n1, const this_type& x) const + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(pos1 > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + return compare( + internalLayout().BeginPtr() + pos1, + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), + x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::compare(size_type pos1, size_type n1, const this_type& x, size_type pos2, size_type n2) const + { + #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())))) + ThrowRangeException(); + #endif + + 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)); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::compare(const value_type* p) const + { + return compare(internalLayout().BeginPtr(), internalLayout().EndPtr(), p, p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::compare(size_type pos1, size_type n1, const value_type* p) const + { + #if EASTL_STRING_OPT_RANGE_ERRORS + if(EASTL_UNLIKELY(pos1 > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + return compare(internalLayout().BeginPtr() + pos1, + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), + p, + p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::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 > internalLayout().GetSize())) + ThrowRangeException(); + #endif + + return compare(internalLayout().BeginPtr() + pos1, + internalLayout().BeginPtr() + pos1 + eastl::min_alt(n1, internalLayout().GetSize() - pos1), + p, + p + n2); + } + + + // make_lower + // This is a very simple ASCII-only case conversion function + // Anything more complicated should use a more powerful separate library. + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::make_lower() + { + for(pointer p = internalLayout().BeginPtr(); p < internalLayout().EndPtr(); ++p) + *p = (value_type)CharToLower(*p); + } + + + // make_upper + // This is a very simple ASCII-only case conversion function + // Anything more complicated should use a more powerful separate library. + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::make_upper() + { + for(pointer p = internalLayout().BeginPtr(); p < internalLayout().EndPtr(); ++p) + *p = (value_type)CharToUpper(*p); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::ltrim() + { + const value_type array[] = { ' ', '\t', 0 }; // This is a pretty simplistic view of whitespace. + erase(0, find_first_not_of(array)); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::rtrim() + { + const value_type array[] = { ' ', '\t', 0 }; // This is a pretty simplistic view of whitespace. + erase(find_last_not_of(array) + 1); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::trim() + { + ltrim(); + rtrim(); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::ltrim(const value_type* p) + { + erase(0, find_first_not_of(p)); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::rtrim(const value_type* p) + { + erase(find_last_not_of(p) + 1); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::trim(const value_type* p) + { + ltrim(p); + rtrim(p); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator> basic_string<T, Allocator>::left(size_type n) const + { + const size_type nLength = length(); + if(n < nLength) + return substr(0, n); + // C++ std says that substr must return default constructed allocated, but we do not. + // Instead it is much more practical to provide the copy of the current allocator + return basic_string(*this, get_allocator()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator> basic_string<T, Allocator>::right(size_type n) const + { + const size_type nLength = length(); + if(n < nLength) + return substr(nLength - n, n); + // C++ std says that substr must return default constructed allocated, but we do not. + // Instead it is much more practical to provide the copy of the current allocator + return basic_string(*this, get_allocator()); + } + + + template <typename T, typename Allocator> + inline basic_string<T, Allocator>& basic_string<T, Allocator>::sprintf(const value_type* pFormat, ...) + { + va_list arguments; + va_start(arguments, pFormat); + internalLayout().SetSize(0); // Fast truncate to zero length. + append_sprintf_va_list(pFormat, arguments); + va_end(arguments); + + return *this; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator>& basic_string<T, Allocator>::sprintf_va_list(const value_type* pFormat, va_list arguments) + { + internalLayout().SetSize(0); // Fast truncate to zero length. + + return append_sprintf_va_list(pFormat, arguments); + } + + + template <typename T, typename Allocator> + int basic_string<T, Allocator>::compare(const value_type* pBegin1, const value_type* pEnd1, + const value_type* pBegin2, const value_type* pEnd2) + { + 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))); + } + + + template <typename T, typename Allocator> + int basic_string<T, Allocator>::comparei(const value_type* pBegin1, const value_type* pEnd1, + const value_type* pBegin2, const value_type* pEnd2) + { + 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))); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::comparei(const this_type& x) const EA_NOEXCEPT + { + return comparei(internalLayout().BeginPtr(), internalLayout().EndPtr(), x.internalLayout().BeginPtr(), x.internalLayout().EndPtr()); + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::comparei(const value_type* p) const + { + return comparei(internalLayout().BeginPtr(), internalLayout().EndPtr(), p, p + CharStrlen(p)); + } + + + template <typename T, typename Allocator> + typename basic_string<T, Allocator>::iterator + basic_string<T, Allocator>::InsertInternal(const_iterator p, value_type c) + { + iterator pNewPosition = const_cast<value_type*>(p); + + if((internalLayout().EndPtr() + 1) <= internalLayout().CapacityPtr()) + { + const size_type nSavedSize = internalLayout().GetSize(); + memmove(const_cast<value_type*>(p) + 1, p, (size_t)(internalLayout().EndPtr() - p) * sizeof(value_type)); + *(internalLayout().EndPtr() + 1) = 0; + *pNewPosition = c; + internalLayout().SetSize(nSavedSize + 1); + } + else + { + const size_type nOldSize = internalLayout().GetSize(); + const size_type nOldCap = capacity(); + const size_type nLength = GetNewCapacity(nOldCap, 1); + + iterator pNewBegin = DoAllocate(nLength + 1); + + pNewPosition = CharStringUninitializedCopy(internalLayout().BeginPtr(), p, pNewBegin); + *pNewPosition = c; + + iterator pNewEnd = pNewPosition + 1; + pNewEnd = CharStringUninitializedCopy(p, internalLayout().EndPtr(), pNewEnd); + *pNewEnd = 0; + + DeallocateSelf(); + internalLayout().SetHeapBeginPtr(pNewBegin); + internalLayout().SetHeapCapacity(nLength); + internalLayout().SetHeapSize(nOldSize + 1); + } + return pNewPosition; + } + + + template <typename T, typename Allocator> + void basic_string<T, Allocator>::SizeInitialize(size_type n, value_type c) + { + AllocateSelf(n); + + CharStringUninitializedFillN(internalLayout().BeginPtr(), n, c); + *internalLayout().EndPtr() = 0; + } + + + template <typename T, typename Allocator> + void basic_string<T, Allocator>::RangeInitialize(const value_type* pBegin, const value_type* pEnd) + { + #if EASTL_STRING_OPT_ARGUMENT_ERRORS + if(EASTL_UNLIKELY(!pBegin && (pEnd < pBegin))) // 21.4.2 p7 + ThrowInvalidArgumentException(); + #endif + + const size_type n = (size_type)(pEnd - pBegin); + + AllocateSelf(n); + + CharStringUninitializedCopy(pBegin, pEnd, internalLayout().BeginPtr()); + *internalLayout().EndPtr() = 0; + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::RangeInitialize(const value_type* pBegin) + { + #if EASTL_STRING_OPT_ARGUMENT_ERRORS + if(EASTL_UNLIKELY(!pBegin)) + ThrowInvalidArgumentException(); + #endif + + RangeInitialize(pBegin, pBegin + CharStrlen(pBegin)); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::DoAllocate(size_type n) + { + return (value_type*)EASTLAlloc(get_allocator(), n * sizeof(value_type)); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::DoFree(value_type* p, size_type n) + { + if(p) + EASTLFree(get_allocator(), p, n * sizeof(value_type)); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::GetNewCapacity(size_type currentCapacity) + { + return GetNewCapacity(currentCapacity, 1); + } + + + template <typename T, typename Allocator> + inline typename basic_string<T, Allocator>::size_type + basic_string<T, Allocator>::GetNewCapacity(size_type currentCapacity, size_type minimumGrowSize) + { + #if EASTL_STRING_OPT_LENGTH_ERRORS + const size_type nRemainingSize = max_size() - currentCapacity; + if(EASTL_UNLIKELY((minimumGrowSize > nRemainingSize))) + { + ThrowLengthException(); + } + #endif + + const size_type nNewCapacity = eastl::max_alt(currentCapacity + minimumGrowSize, currentCapacity * 2); + + return nNewCapacity; + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::AllocateSelf() + { + internalLayout().ResetToSSO(); + } + + + template <typename T, typename Allocator> + void basic_string<T, Allocator>::AllocateSelf(size_type n) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(n >= 0x40000000)) + EASTL_FAIL_MSG("basic_string::AllocateSelf -- improbably large request."); + #endif + + #if EASTL_STRING_OPT_LENGTH_ERRORS + if(EASTL_UNLIKELY(n > max_size())) + ThrowLengthException(); + #endif + + if(n > SSOLayout::SSO_CAPACITY) + { + pointer pBegin = DoAllocate(n + 1); + internalLayout().SetHeapBeginPtr(pBegin); + internalLayout().SetHeapCapacity(n); + internalLayout().SetHeapSize(n); + } + else + internalLayout().SetSSOSize(n); + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::DeallocateSelf() + { + if(internalLayout().IsHeap()) + { + DoFree(internalLayout().BeginPtr(), internalLayout().GetHeapCapacity() + 1); + } + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::ThrowLengthException() const + { + #if EASTL_EXCEPTIONS_ENABLED + throw std::length_error("basic_string -- length_error"); + #elif EASTL_ASSERT_ENABLED + EASTL_FAIL_MSG("basic_string -- length_error"); + #endif + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::ThrowRangeException() const + { + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("basic_string -- out of range"); + #elif EASTL_ASSERT_ENABLED + EASTL_FAIL_MSG("basic_string -- out of range"); + #endif + } + + + template <typename T, typename Allocator> + inline void basic_string<T, Allocator>::ThrowInvalidArgumentException() const + { + #if EASTL_EXCEPTIONS_ENABLED + throw std::invalid_argument("basic_string -- invalid argument"); + #elif EASTL_ASSERT_ENABLED + EASTL_FAIL_MSG("basic_string -- invalid argument"); + #endif + } + + + // CharTypeStringFindEnd + // Specialized char version of STL find() from back function. + // Not the same as RFind because search range is specified as forward iterators. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::CharTypeStringFindEnd(const value_type* pBegin, const value_type* pEnd, value_type c) + { + const value_type* pTemp = pEnd; + while(--pTemp >= pBegin) + { + if(*pTemp == c) + return pTemp; + } + + return pEnd; + } + + + // CharTypeStringRFind + // Specialized value_type version of STL find() function in reverse. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::CharTypeStringRFind(const value_type* pRBegin, const value_type* pREnd, const value_type c) + { + while(pRBegin > pREnd) + { + if(*(pRBegin - 1) == c) + return pRBegin; + --pRBegin; + } + return pREnd; + } + + + // CharTypeStringSearch + // Specialized value_type version of STL search() function. + // Purpose: find p2 within p1. Return p1End if not found or if either string is zero length. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::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, + // but the return value is the same either way. + if((p1Begin == p1End) || (p2Begin == p2End)) + return p1Begin; + + // Test for a pattern of length 1. + if((p2Begin + 1) == p2End) + return eastl::find(p1Begin, p1End, *p2Begin); + + // General case. + const value_type* pTemp; + const value_type* pTemp1 = (p2Begin + 1); + const value_type* pCurrent = p1Begin; + + while(p1Begin != p1End) + { + p1Begin = eastl::find(p1Begin, p1End, *p2Begin); + if(p1Begin == p1End) + return p1End; + + pTemp = pTemp1; + pCurrent = p1Begin; + if(++pCurrent == p1End) + return p1End; + + while(*pCurrent == *pTemp) + { + if(++pTemp == p2End) + return p1Begin; + if(++pCurrent == p1End) + return p1End; + } + + ++p1Begin; + } + + return p1Begin; + } + + + // CharTypeStringRSearch + // 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 <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::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, + // but the return value is the same either way. + if((p1Begin == p1End) || (p2Begin == p2End)) + return p1Begin; + + // Test for a pattern of length 1. + if((p2Begin + 1) == p2End) + return CharTypeStringFindEnd(p1Begin, p1End, *p2Begin); + + // Test for search string length being longer than string length. + if((p2End - p2Begin) > (p1End - p1Begin)) + return p1End; + + // General case. + const value_type* pSearchEnd = (p1End - (p2End - p2Begin) + 1); + const value_type* pCurrent1; + const value_type* pCurrent2; + + while(pSearchEnd != p1Begin) + { + // Search for the last occurrence of *p2Begin. + pCurrent1 = CharTypeStringFindEnd(p1Begin, pSearchEnd, *p2Begin); + 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. + pCurrent2 = p2Begin; + while(*pCurrent1++ == *pCurrent2++) + { + if(pCurrent2 == p2End) + return (pCurrent1 - (p2End - p2Begin)); + } + + // A smarter algorithm might know to subtract more than just one, + // but in most cases it won't make much difference anyway. + --pSearchEnd; + } + + return p1End; + } + + + // CharTypeStringFindFirstOf + // Specialized value_type version of STL find_first_of() function. + // This function is much like the C runtime strtok function, except the strings aren't null-terminated. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::CharTypeStringFindFirstOf(const value_type* p1Begin, const value_type* p1End, + const value_type* p2Begin, const value_type* p2End) + { + for( ; p1Begin != p1End; ++p1Begin) + { + for(const value_type* pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if(*p1Begin == *pTemp) + return p1Begin; + } + } + return p1End; + } + + + // CharTypeStringRFindFirstOf + // Specialized value_type version of STL find_first_of() function in reverse. + // This function is much like the C runtime strtok function, except the strings aren't null-terminated. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::CharTypeStringRFindFirstOf(const value_type* p1RBegin, const value_type* p1REnd, + const value_type* p2Begin, const value_type* p2End) + { + for( ; p1RBegin != p1REnd; --p1RBegin) + { + for(const value_type* pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if(*(p1RBegin - 1) == *pTemp) + return p1RBegin; + } + } + return p1REnd; + } + + + + // CharTypeStringFindFirstNotOf + // Specialized value_type version of STL find_first_not_of() function. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::CharTypeStringFindFirstNotOf(const value_type* p1Begin, const value_type* p1End, + const value_type* p2Begin, const value_type* p2End) + { + for( ; p1Begin != p1End; ++p1Begin) + { + const value_type* pTemp; + for(pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if(*p1Begin == *pTemp) + break; + } + if(pTemp == p2End) + return p1Begin; + } + return p1End; + } + + + // CharTypeStringRFindFirstNotOf + // Specialized value_type version of STL find_first_not_of() function in reverse. + template <typename T, typename Allocator> + const typename basic_string<T, Allocator>::value_type* + basic_string<T, Allocator>::CharTypeStringRFindFirstNotOf(const value_type* p1RBegin, const value_type* p1REnd, + const value_type* p2Begin, const value_type* p2End) + { + for( ; p1RBegin != p1REnd; --p1RBegin) + { + const value_type* pTemp; + for(pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if(*(p1RBegin-1) == *pTemp) + break; + } + if(pTemp == p2End) + return p1RBegin; + } + return p1REnd; + } + + + + + // iterator operators + template <typename T, typename Allocator> + inline bool operator==(const typename basic_string<T, Allocator>::reverse_iterator& r1, + const typename basic_string<T, Allocator>::reverse_iterator& r2) + { + return r1.mpCurrent == r2.mpCurrent; + } + + + template <typename T, typename Allocator> + inline bool operator!=(const typename basic_string<T, Allocator>::reverse_iterator& r1, + const typename basic_string<T, Allocator>::reverse_iterator& r2) + { + return r1.mpCurrent != r2.mpCurrent; + } + + + // Operator + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + typedef typename basic_string<T, Allocator>::CtorDoNotInitialize CtorDoNotInitialize; + CtorDoNotInitialize cDNI; // GCC 2.x forces us to declare a named temporary like this. + basic_string<T, Allocator> result(cDNI, a.size() + b.size(), const_cast<basic_string<T, Allocator>&>(a).get_allocator()); // Note that we choose to assign a's allocator. + result.append(a); + result.append(b); + return result; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + typedef typename basic_string<T, Allocator>::CtorDoNotInitialize CtorDoNotInitialize; + CtorDoNotInitialize cDNI; // GCC 2.x forces us to declare a named temporary like this. + const typename basic_string<T, Allocator>::size_type n = (typename basic_string<T, Allocator>::size_type)CharStrlen(p); + basic_string<T, Allocator> result(cDNI, n + b.size(), const_cast<basic_string<T, Allocator>&>(b).get_allocator()); + result.append(p, p + n); + result.append(b); + return result; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(typename basic_string<T, Allocator>::value_type c, const basic_string<T, Allocator>& b) + { + typedef typename basic_string<T, Allocator>::CtorDoNotInitialize CtorDoNotInitialize; + CtorDoNotInitialize cDNI; // GCC 2.x forces us to declare a named temporary like this. + basic_string<T, Allocator> result(cDNI, 1 + b.size(), const_cast<basic_string<T, Allocator>&>(b).get_allocator()); + result.push_back(c); + result.append(b); + return result; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + typedef typename basic_string<T, Allocator>::CtorDoNotInitialize CtorDoNotInitialize; + CtorDoNotInitialize cDNI; // GCC 2.x forces us to declare a named temporary like this. + const typename basic_string<T, Allocator>::size_type n = (typename basic_string<T, Allocator>::size_type)CharStrlen(p); + basic_string<T, Allocator> result(cDNI, a.size() + n, const_cast<basic_string<T, Allocator>&>(a).get_allocator()); + result.append(a); + result.append(p, p + n); + return result; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(const basic_string<T, Allocator>& a, typename basic_string<T, Allocator>::value_type c) + { + typedef typename basic_string<T, Allocator>::CtorDoNotInitialize CtorDoNotInitialize; + CtorDoNotInitialize cDNI; // GCC 2.x forces us to declare a named temporary like this. + basic_string<T, Allocator> result(cDNI, a.size() + 1, const_cast<basic_string<T, Allocator>&>(a).get_allocator()); + result.append(a); + result.push_back(c); + return result; + } + + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(basic_string<T, Allocator>&& a, basic_string<T, Allocator>&& b) + { + a.append(b); // Using an rvalue by name results in it becoming an lvalue. + return eastl::move(a); + } + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(basic_string<T, Allocator>&& a, const basic_string<T, Allocator>& b) + { + a.append(b); + return eastl::move(a); + } + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(const typename basic_string<T, Allocator>::value_type* p, basic_string<T, Allocator>&& b) + { + b.insert(0, p); + return eastl::move(b); + } + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(basic_string<T, Allocator>&& a, const typename basic_string<T, Allocator>::value_type* p) + { + a.append(p); + return eastl::move(a); + } + + template <typename T, typename Allocator> + basic_string<T, Allocator> operator+(basic_string<T, Allocator>&& a, typename basic_string<T, Allocator>::value_type c) + { + a.push_back(c); + return eastl::move(a); + } + + + template <typename T, typename Allocator> + inline bool basic_string<T, Allocator>::validate() const EA_NOEXCEPT + { + if((internalLayout().BeginPtr() == nullptr) || (internalLayout().EndPtr() == nullptr)) + return false; + if(internalLayout().EndPtr() < internalLayout().BeginPtr()) + return false; + if(internalLayout().CapacityPtr() < internalLayout().EndPtr()) + return false; + if(*internalLayout().EndPtr() != 0) + return false; + return true; + } + + + template <typename T, typename Allocator> + inline int basic_string<T, Allocator>::validate_iterator(const_iterator i) const EA_NOEXCEPT + { + if(i >= internalLayout().BeginPtr()) + { + if(i < internalLayout().EndPtr()) + return (isf_valid | isf_current | isf_can_dereference); + + if(i <= internalLayout().EndPtr()) + return (isf_valid | isf_current); + } + + return isf_none; + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + // Operator== and operator!= + template <typename T, typename Allocator> + inline bool operator==(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return ((a.size() == b.size()) && (memcmp(a.data(), b.data(), (size_t)a.size() * sizeof(typename basic_string<T, Allocator>::value_type)) == 0)); + } + + + template <typename T, typename Allocator> + inline bool operator==(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + typedef typename basic_string<T, Allocator>::size_type size_type; + const size_type n = (size_type)CharStrlen(p); + return ((n == b.size()) && (memcmp(p, b.data(), (size_t)n * sizeof(*p)) == 0)); + } + + + template <typename T, typename Allocator> + inline bool operator==(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + typedef typename basic_string<T, Allocator>::size_type size_type; + const size_type n = (size_type)CharStrlen(p); + return ((a.size() == n) && (memcmp(a.data(), p, (size_t)n * sizeof(*p)) == 0)); + } + + + template <typename T, typename Allocator> + inline bool operator!=(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return !(a == b); + } + + + template <typename T, typename Allocator> + inline bool operator!=(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + return !(p == b); + } + + + template <typename T, typename Allocator> + inline bool operator!=(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + return !(a == p); + } + + + // Operator< (and also >, <=, and >=). + template <typename T, typename Allocator> + inline bool operator<(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return basic_string<T, Allocator>::compare(a.begin(), a.end(), b.begin(), b.end()) < 0; } + + + template <typename T, typename Allocator> + inline bool operator<(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + typedef typename basic_string<T, Allocator>::size_type size_type; + const size_type n = (size_type)CharStrlen(p); + return basic_string<T, Allocator>::compare(p, p + n, b.begin(), b.end()) < 0; + } + + + template <typename T, typename Allocator> + inline bool operator<(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + typedef typename basic_string<T, Allocator>::size_type size_type; + const size_type n = (size_type)CharStrlen(p); + return basic_string<T, Allocator>::compare(a.begin(), a.end(), p, p + n) < 0; + } + + + template <typename T, typename Allocator> + inline bool operator>(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return b < a; + } + + + template <typename T, typename Allocator> + inline bool operator>(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + return b < p; + } + + + template <typename T, typename Allocator> + inline bool operator>(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + return p < a; + } + + + template <typename T, typename Allocator> + inline bool operator<=(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return !(b < a); + } + + + template <typename T, typename Allocator> + inline bool operator<=(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + return !(b < p); + } + + + template <typename T, typename Allocator> + inline bool operator<=(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + return !(p < a); + } + + + template <typename T, typename Allocator> + inline bool operator>=(const basic_string<T, Allocator>& a, const basic_string<T, Allocator>& b) + { + return !(a < b); + } + + + template <typename T, typename Allocator> + inline bool operator>=(const typename basic_string<T, Allocator>::value_type* p, const basic_string<T, Allocator>& b) + { + return !(p < b); + } + + + template <typename T, typename Allocator> + inline bool operator>=(const basic_string<T, Allocator>& a, const typename basic_string<T, Allocator>::value_type* p) + { + return !(a < p); + } + + + template <typename T, typename Allocator> + inline void swap(basic_string<T, Allocator>& a, basic_string<T, Allocator>& b) + { + a.swap(b); + } + + + /// string / wstring + typedef basic_string<char> string; + typedef basic_string<wchar_t> wstring; + + /// custom string8 / string16 / string32 + typedef basic_string<char> string8; + typedef basic_string<char16_t> string16; + typedef basic_string<char32_t> string32; + + /// ISO mandated string types + typedef basic_string<char8_t> u8string; // Actually not a C++11 type, but added for consistency. + typedef basic_string<char16_t> u16string; + typedef basic_string<char32_t> u32string; + + + /// hash<string> + /// + /// We provide EASTL hash function objects for use in hash table containers. + /// + /// Example usage: + /// #include <EASTL/hash_set.h> + /// hash_set<string> stringHashSet; + /// + template <typename T> struct hash; + + template <> + struct hash<string> + { + 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. + while((c = *p++) != 0) // Using '!=' disables compiler warnings. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + template <> + struct hash<u8string> + { + size_t operator()(const u8string& x) const + { + const char8_t* p = (const char8_t*)x.c_str(); + unsigned int c, result = 2166136261U; + while((c = *p++) != 0) + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + #endif + + template <> + struct hash<string16> + { + size_t operator()(const string16& x) const + { + const char16_t* p = x.c_str(); + unsigned int c, result = 2166136261U; + while((c = *p++) != 0) + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template <> + struct hash<string32> + { + size_t operator()(const string32& x) const + { + const char32_t* p = x.c_str(); + unsigned int c, result = 2166136261U; + while((c = (unsigned int)*p++) != 0) + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + #if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE + template <> + struct hash<wstring> + { + size_t operator()(const wstring& x) const + { + const wchar_t* p = x.c_str(); + unsigned int c, result = 2166136261U; + while((c = (unsigned int)*p++) != 0) + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + #endif + + + /// 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. + /// + /// http://en.cppreference.com/w/cpp/string/basic_string/to_string + /// + inline string to_string(int value) + { return string(string::CtorSprintf(), "%d", value); } + inline string to_string(long value) + { return string(string::CtorSprintf(), "%ld", value); } + inline string to_string(long long value) + { return string(string::CtorSprintf(), "%lld", value); } + inline string to_string(unsigned value) + { return string(string::CtorSprintf(), "%u", value); } + inline string to_string(unsigned long value) + { return string(string::CtorSprintf(), "%lu", value); } + inline string to_string(unsigned long long value) + { return string(string::CtorSprintf(), "%llu", value); } + inline string to_string(float value) + { return string(string::CtorSprintf(), "%f", value); } + inline string to_string(double value) + { return string(string::CtorSprintf(), "%f", value); } + inline string to_string(long double value) + { return string(string::CtorSprintf(), "%Lf", value); } + + + /// 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. + /// + /// http://en.cppreference.com/w/cpp/string/basic_string/to_wstring + /// + inline wstring to_wstring(int value) + { return wstring(wstring::CtorSprintf(), L"%d", value); } + inline wstring to_wstring(long value) + { return wstring(wstring::CtorSprintf(), L"%ld", value); } + inline wstring to_wstring(long long value) + { return wstring(wstring::CtorSprintf(), L"%lld", value); } + inline wstring to_wstring(unsigned value) + { return wstring(wstring::CtorSprintf(), L"%u", value); } + inline wstring to_wstring(unsigned long value) + { return wstring(wstring::CtorSprintf(), L"%lu", value); } + inline wstring to_wstring(unsigned long long value) + { return wstring(wstring::CtorSprintf(), L"%llu", value); } + inline wstring to_wstring(float value) + { return wstring(wstring::CtorSprintf(), L"%f", value); } + inline wstring to_wstring(double value) + { return wstring(wstring::CtorSprintf(), L"%f", value); } + inline wstring to_wstring(long double value) + { return wstring(wstring::CtorSprintf(), L"%Lf", value); } + + + /// user defined literals + /// + /// Converts a character array literal to a basic_string. + /// + /// Example: + /// string s = "abcdef"s; + /// + /// http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s + /// + #if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED + EA_DISABLE_VC_WARNING(4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved + inline namespace literals + { + inline namespace string_literals + { + inline string operator"" s(const char* str, size_t len) EA_NOEXCEPT { return {str, string::size_type(len)}; } + inline u16string operator"" s(const char16_t* str, size_t len) EA_NOEXCEPT { return {str, u16string::size_type(len)}; } + inline u32string operator"" s(const char32_t* str, size_t len) EA_NOEXCEPT { return {str, u32string::size_type(len)}; } + inline wstring operator"" s(const wchar_t* str, size_t len) EA_NOEXCEPT { return {str, wstring::size_type(len)}; } + + // C++20 char8_t support. + #if EA_CHAR8_UNIQUE + inline u8string operator"" s(const char8_t* str, size_t len) EA_NOEXCEPT { return {str, u8string::size_type(len)}; } + #endif + } + } + EA_RESTORE_VC_WARNING() // warning: 4455 + #endif + + + /// erase / erase_if + /// + /// https://en.cppreference.com/w/cpp/string/basic_string/erase2 + template <class CharT, class Allocator, class U> + void erase(basic_string<CharT, Allocator>& c, const U& value) + { + // Erases all elements that compare equal to value from the container. + c.erase(eastl::remove(c.begin(), c.end(), value), c.end()); + } + + template <class CharT, class Allocator, class Predicate> + void erase_if(basic_string<CharT, Allocator>& c, Predicate predicate) + { + // Erases all elements that satisfy the predicate pred from the container. + c.erase(eastl::remove_if(c.begin(), c.end(), predicate), c.end()); + } +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard |