diff --git a/Code/Framework/AzCore/AzCore/PlatformDef.h b/Code/Framework/AzCore/AzCore/PlatformDef.h index 8d28d27959..7f00f7e90e 100644 --- a/Code/Framework/AzCore/AzCore/PlatformDef.h +++ b/Code/Framework/AzCore/AzCore/PlatformDef.h @@ -149,3 +149,79 @@ #if !defined(AZ_COMMAND_LINE_LEN) # define AZ_COMMAND_LINE_LEN 2048 #endif + +#include +#include +#include +#include +#include + +// First check if the feature if is_constant_evaluated is available via the feature test macro +// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros#C.2B.2B20 +#if __cpp_lib_is_constant_evaluated + #define az_builtin_is_constant_evaluated() std::is_constant_evaluated() +#endif + +// Next check if there is a __builtin_is_constant_evaluated that can be used +// This works on MSVC 19.28+ toolsets when using C++17, as well as +// clang 9.0.0+ when using C++17. +// Finally it works on gcc 9.0+ when using C++17 +#if !defined(az_builtin_is_constant_evaluated) + #if defined(__has_builtin) + #if __has_builtin(__builtin_is_constant_evaluated) + #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() true + #endif + #elif AZ_COMPILER_MSVC >= 1928 + #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() true + #elif AZ_COMPILER_GCC + #define az_builtin_is_constant_evaluated() __builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() true + #endif +#endif + +// In this case no support for the determining whether an operation is occuring +// at compile time is supported so assume that evaluation is always occuring at compile time +// in order to make sure the "safe" operation is being performed +#if !defined(az_builtin_is_constant_evaluated) + namespace AZ::Internal + { + constexpr bool builtin_is_constant_evaluated() + { + return true; + } + } + #define az_builtin_is_constant_evaluated() AZ::Internal::builtin_is_constant_evaluated() + #define az_has_builtin_is_constant_evaluated() false +#endif + +// define builtin functions used by char_traits class for efficient compile time and runtime +// operations +#if defined(__has_builtin) + #if __has_builtin(__builtin_memcpy) + #define az_has_builtin_memcpy true + #endif + #if __has_builtin(__builtin_wmemcpy) + #define az_has_builtin_wmemcpy true + #endif + #if __has_builtin(__builtin_memmove) + #define az_has_builtin_memmove true + #endif + #if __has_builtin(__builtin_wmemmove) + #define az_has_builtin_wmemmove true + #endif +#endif + +#if !defined(az_has_builtin_memcpy) + #define az_has_builtin_memcpy false +#endif +#if !defined(az_has_builtin_wmemcpy) + #define az_has_builtin_wmemcpy false +#endif +#if !defined(az_has_builtin_memmove) + #define az_has_builtin_memmove false +#endif +#if !defined(az_has_builtin_wmemmove) + #define az_has_builtin_wmemmove false +#endif diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp index 5806cc485c..baf650a560 100644 --- a/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.cpp @@ -11,17 +11,17 @@ namespace AZStd { - stateless_allocator::stateless_allocator(const char* name) - : m_name(name) {} + stateless_allocator::stateless_allocator() = default; + stateless_allocator::stateless_allocator(const char*) + {} const char* stateless_allocator::get_name() const { - return m_name; + return "AZStd::stateless_allocator"; } - void stateless_allocator::set_name(const char* name) + void stateless_allocator::set_name(const char*) { - m_name = name; } auto stateless_allocator::allocate(size_type byteSize) -> pointer_type diff --git a/Code/Framework/AzCore/AzCore/std/allocator_stateless.h b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h index b73c680c32..6b78aca53d 100644 --- a/Code/Framework/AzCore/AzCore/std/allocator_stateless.h +++ b/Code/Framework/AzCore/AzCore/std/allocator_stateless.h @@ -26,7 +26,8 @@ namespace AZStd using difference_type = ptrdiff_t; using allow_memory_leaks = AZStd::true_type; - stateless_allocator(const char* name = "AZStd::stateless_allocator"); + stateless_allocator(); + explicit stateless_allocator(const char*); // Stateless allocator does not store a name stateless_allocator(const stateless_allocator& rhs) = default; stateless_allocator& operator=(const stateless_allocator& rhs) = default; @@ -51,9 +52,6 @@ namespace AZStd bool is_lock_free(); bool is_stale_read_allowed(); bool is_delayed_recycling(); - - private: - const char* m_name; }; bool operator==(const stateless_allocator& left, const stateless_allocator& right); diff --git a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h index 066ec7be5e..c79ddf11ee 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h +++ b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include /* Microsoft C++ ABI puts 1 byte of padding between each empty base class when multiple inheritance is being used @@ -20,7 +21,7 @@ #if defined(AZ_COMPILER_MSVC) #define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION __declspec(empty_bases) #else -#define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION +#define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION #endif namespace AZStd @@ -97,16 +98,14 @@ namespace AZStd using second_base_value_type = typename second_base_type::value_type; public: - // First template argument is a placeholder argument of void as MSVC examines the types - // of a templated function to determine if they are the same template - // Due to the "template compressed_pair(skip_element_tag, T&&)" - // constructor below, the default constructor template types needs to be distinguished from it - template ::value - && AZStd::is_default_constructible::value>> + // First template argument is used to perform a substitution into AZStd::enable_if_t + // so that SFINAE can trigger + template + && AZStd::is_default_constructible_v, Unused>> constexpr compressed_pair(); - template , compressed_pair>::value, bool> = true> + template , compressed_pair>, bool> = true> constexpr explicit compressed_pair(T&& firstElement); template diff --git a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl index 9fb5eb87fb..8e585a1467 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl +++ b/Code/Framework/AzCore/AzCore/std/containers/compressed_pair.inl @@ -75,7 +75,7 @@ namespace AZStd } template - template , compressed_pair>::value, bool>> + template , compressed_pair>, bool>> inline constexpr compressed_pair::compressed_pair(T&& firstElement) : first_base_type{ AZStd::forward(firstElement) } , second_base_type{} @@ -117,7 +117,7 @@ namespace AZStd { return static_cast(*this).get(); } - + template inline constexpr auto compressed_pair::second() -> second_base_value_type& { diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h index a8e0f96988..ea841bc4ca 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.h +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.h @@ -343,26 +343,6 @@ namespace AZStd static decltype(auto) format(const wchar_t* format, ...); protected: - template - constexpr auto append_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; - - template - constexpr auto construct_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v>; - - template - constexpr auto assign_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; - - template - constexpr auto insert_iter(const_iterator insertPos, InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, iterator>; - - template - constexpr auto replace_iter(const_iterator first, const_iterator last, InputIt first2, InputIt last2) - -> enable_if_t && !is_convertible_v, basic_fixed_string&>; - constexpr auto fits_in_capacity(size_type newSize) -> bool; inline static constexpr size_type Capacity = MaxElementCount; // current storage reserved for string not including null-terminator diff --git a/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl b/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl index 8b973d3b2c..15d2acf7a1 100644 --- a/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl +++ b/Code/Framework/AzCore/AzCore/std/string/fixed_string.inl @@ -62,14 +62,7 @@ namespace AZStd template inline constexpr basic_fixed_string::basic_fixed_string(InputIt first, InputIt last) { // construct from [first, last) - if (first == last) - { - Traits::assign(m_buffer[0], Element()); // terminate - } - else - { - construct_iter(first, last); - } + assign(first, last); } // #7 @@ -98,8 +91,7 @@ namespace AZStd template inline constexpr basic_fixed_string::basic_fixed_string(const T& convertibleToView) { - AZStd::basic_string_view view = convertibleToView; - assign(view.begin(), view.end()); + assign(convertibleToView); } // #11 @@ -313,15 +305,7 @@ namespace AZStd if (count > 0 && fits_in_capacity(num)) { pointer data = m_buffer; - // make room and append new stuff using assign - if (count == 1) - { - Traits::assign(*(data + m_size), ch); - } - else - { - Traits::assign(data + m_size, count, ch); - } + Traits::assign(data + m_size, count, ch); m_size = static_cast(num); Traits::assign(data[num], Element()); // terminate } @@ -332,13 +316,47 @@ namespace AZStd template inline constexpr auto basic_fixed_string::append(InputIt first, InputIt last) -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // append [first, last) - return append_iter(first, last); + { + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return append(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + size_type newSize = m_size + AZStd::distance(first, last); + if (fits_in_capacity(newSize)) + { + for (size_t updateIndex = m_size; first != last; ++first, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*first)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return append(inputCopy.c_str(), inputCopy.size()); + } } template inline constexpr auto basic_fixed_string::append(AZStd::initializer_list ilist) -> basic_fixed_string& - { // append [first, last) - return append_iter(ilist.begin(), ilist.end()); + { + return append(ilist.begin(), ilist.size()); } template @@ -420,18 +438,10 @@ namespace AZStd inline constexpr auto basic_fixed_string::assign(size_type count, Element ch) -> basic_fixed_string& { // assign count * ch - AZSTD_CONTAINER_ASSERT(count != npos, "result is too long!"); if (fits_in_capacity(count)) { // make room and assign new stuff pointer data = m_buffer; - if (count == 1) - { - Traits::assign(*(data), ch); - } - else - { - Traits::assign(data, count, ch); - } + Traits::assign(data, count, ch); m_size = static_cast(count); Traits::assign(data[count], Element()); // terminate } @@ -443,12 +453,46 @@ namespace AZStd inline constexpr auto basic_fixed_string::assign(InputIt first, InputIt last) -> enable_if_t && !is_convertible_v, basic_fixed_string&> { - return assign_iter(first, last); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return assign(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be assigned one by one into the buffer + size_type newSize = AZStd::distance(first, last); + if (fits_in_capacity(newSize)) + { + for (size_t updateIndex = 0; first != last; ++first, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*first)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return assign(inputCopy.c_str(), inputCopy.size()); + } } template inline constexpr auto basic_fixed_string::assign(AZStd::initializer_list ilist) -> basic_fixed_string& { - return assign_iter(ilist.begin(), ilist.end()); + return assign(ilist.begin(), ilist.size()); } template @@ -536,14 +580,7 @@ namespace AZStd pointer data = m_buffer; // make room and insert new stuff Traits::copy_backward(data + offset + count, data + offset, m_size - offset); // empty out hole - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, count, ch); - } + Traits::assign(data + offset, count, ch); m_size = static_cast(num); Traits::assign(data[num], Element()); // terminate } @@ -582,14 +619,51 @@ namespace AZStd inline constexpr auto basic_fixed_string::insert(const_iterator insertPos, InputIt first, InputIt last)-> enable_if_t && !is_convertible_v, iterator> { // insert [_First, _Last) at _Where - return insert_iter(insertPos, first, last); + size_type insertOffset = AZStd::distance(cbegin(), insertPos); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be inserted one by one into the buffer + size_type count = AZStd::distance(first, last); + size_type newSize = m_size + count; + if (fits_in_capacity(newSize)) + { + Traits::copy_backward(m_buffer + insertOffset + count, m_buffer + insertOffset, m_size - insertOffset); // empty out hole + for (size_t updateIndex = insertOffset; first != last; ++first, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*first)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + insert(insertOffset, inputCopy.c_str(), inputCopy.size()); + } + return begin() + insertOffset; } template inline constexpr auto basic_fixed_string::insert(const_iterator insertPos, AZStd::initializer_list ilist) -> iterator { // insert [_First, _Last) at _Where - return insert_iter(insertPos, ilist.begin(), ilist.end()); + return insert(insertPos, ilist.begin(), ilist.end()); } template @@ -604,7 +678,7 @@ namespace AZStd { // move elements down pointer data = m_buffer; - Traits::copy(data + offset, data + offset + count, m_size - offset - count); + Traits::move(data + offset, data + offset + count, m_size - offset - count); m_size = static_cast(m_size - count); Traits::assign(data[m_size], Element()); // terminate } @@ -643,7 +717,7 @@ namespace AZStd const basic_fixed_string& rhs) -> basic_fixed_string& { // replace [offset, offset + count) with rhs - return replace(offset, count, rhs, size_type(0), npos); + return replace(offset, count, rhs.c_str(), rhs.size()); } template @@ -651,56 +725,7 @@ namespace AZStd const basic_fixed_string& rhs, size_type rhsOffset, size_type rhsCount) -> basic_fixed_string& { // replace [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) - AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offsets"); - if (m_size - offset < count) - { - count = m_size - offset; // trim count to size - } - size_type num = rhs.m_size - rhsOffset; - if (num < rhsCount) - { - rhsCount = num; // trim rhsCount to size - } - AZSTD_CONTAINER_ASSERT(npos - rhsCount > m_size - count, "Result is too long"); - - size_type nm = m_size - count - offset; // length of preserved tail - size_type newSize = m_size + rhsCount - count; - if (fits_in_capacity(newSize)) - { - pointer data = m_buffer; - const_pointer rhsData = rhs.m_buffer; - - if (this != &rhs) - { // no overlap, just move down and copy in new stuff - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // empty hole - Traits::copy(data + offset, rhsData + rhsOffset, rhsCount); // fill hole - } - else if (rhsCount <= count) - { // hole doesn't get larger, just copy in substring - Traits::copy(data + offset, data + rhsOffset, rhsCount); // fill hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - } - else if (rhsOffset <= offset) - { // hole gets larger, substring begins before hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - Traits::copy(data + offset, data + rhsOffset, rhsCount); // fill hole - } - else if (offset + count <= rhsOffset) - { // hole gets larger, substring begins after hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - Traits::copy(data + offset, data + (rhsOffset + rhsCount - count), rhsCount); // fill hole - } - else - { // hole gets larger, substring begins in hole - Traits::copy(data + offset, data + rhsOffset, count); // fill old hole - Traits::copy_backward(data + offset + rhsCount, data + offset + count, nm); // move tail down - Traits::copy(data + offset + count, data + rhsOffset + rhsCount, rhsCount - count); // fill rest of new hole - } - - m_size = static_cast(newSize); - Traits::assign(data[newSize], Element()); // terminate - } - return *this; + return replace(offset, count, rhs.c_str() + rhsOffset, AZStd::min(rhsCount, rhs.size() - rhsOffset)); } template template @@ -720,35 +745,83 @@ namespace AZStd pointer data = m_buffer; // replace [offset, offset + count) with [ptr, ptr + ptrCount) AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) - { - count = m_size - offset; // trim _N0 to size - } - AZSTD_CONTAINER_ASSERT(npos - ptrCount > m_size - count, "Result too long"); + // Make sure count is within is no larger than the distance from the offset + // to the end of this string + count = AZStd::min(count, m_size - offset); - size_type nm = m_size - count - offset; - if (ptrCount < count) - { - Traits::copy(data + offset + ptrCount, data + offset + count, nm); // smaller hole, move tail up - } - size_type num = m_size + ptrCount - count; - if ((0 != ptrCount || 0 != count) && fits_in_capacity(num)) + size_type newSize = m_size + ptrCount - count; + if (fits_in_capacity(newSize)) { - data = m_buffer; - // make room and rearrange - if (count < ptrCount) + // The code assumes that compile time evaluation will not need to deal with overlapping input + size_type charsAfterCountToMove = m_size - count - offset; + if (az_builtin_is_constant_evaluated() || !((ptr >= data + offset && ptr < data + offset + count) + || (ptr + ptrCount > data + offset && ptr + ptrCount <= data + offset + count))) { - Traits::copy_backward(data + offset + ptrCount, data + offset + count, nm); // move tail down + // Ex1. this = "ABCDEFG", offset = 1, count = 4 + // Input string is "CDE" + // First the text post offset + count is moved to right after the input string will be copied + // "ABCDFG" + // ^^^ + // Next the input string is copied into the buffer + // "ACDEFG" + // + // Ex2. this = "ABCDEFG", offset = 1, count = 2 + // Input string is "CDE" + // Performing the same two steps above, the string transform as follows + // "ABCDEFG" -> "ABCDDEFG" -> "ACDEDEFG" + // ^^^ + if (count != ptrCount) + { + Traits::move(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); + } + if (ptrCount > 0) + { + // Copy bytes up to the minimum of this string count and input string count + Traits::copy(data + offset, ptr, ptrCount); + } } - - if (ptrCount > 0) + else { - Traits::copy(data + offset, ptr, ptrCount); // fill hole + // Overlap checks for fixed_string only needs to check between this string + // [offset, offset + count) due to fixed_string never moving memory + // + // Ex. this = "ABCDEFG", offset = 1, count=4 + // substring is "CDE" + // The text from offset 1 for 4 chars "BCDE": should be replaced with "CDE" + // making a whole for the bytes results in output = "ABCDFG" + // Afterwards output = "ACDEFG" + // The input string overlaps with this string in this case + // So the string is copied piecewise + if (ptrCount <= count) + { // hole doesn't get larger, just copy in substring + Traits::move(data + offset, ptr, ptrCount); // fill hole + Traits::copy(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + } + else + { + if (ptr <= data + offset) + { // hole gets larger, substring begins before hole + Traits::copy_backward(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(data + offset, ptr, ptrCount); // fill hole + } + else if (data + offset + count <= ptr) + { // hole gets larger, substring begins after hole + Traits::copy_backward(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(data + offset, ptr + (ptrCount - count), ptrCount); // fill hole + } + else + { // hole gets larger, substring begins in hole + Traits::copy(data + offset, ptr, count); // fill old hole + Traits::copy_backward(data + offset + ptrCount, data + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(data + offset + count, ptr + ptrCount, ptrCount - count); // fill rest of new hole + } + } } - - m_size = static_cast(num); - Traits::assign(data[num], Element()); // terminate } + + m_size = static_cast(newSize); + Traits::assign(data[newSize], Element()); // terminate + return *this; } @@ -793,14 +866,7 @@ namespace AZStd { Traits::copy_backward(data + offset + num, data + offset + count, nm); // move tail down } - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, num, ch); - } + Traits::assign(data + offset, num, ch); m_size = static_cast(numToGrow); Traits::assign(data[numToGrow], Element()); // terminate } @@ -851,15 +917,54 @@ namespace AZStd template template inline constexpr auto basic_fixed_string::replace(const_iterator first, const_iterator last, - InputIt first2, InputIt last2) -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // replace [first, last) with [first2,last2) - return replace_iter(first, last, first2, last2); + InputIt replaceFirst, InputIt replaceLast) -> enable_if_t && !is_convertible_v, basic_fixed_string&> + { // replace [first, last) with [replaceFirst,replaceLast) + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + + size_type insertOffset = AZStd::distance(cbegin(), first); + size_type postInsertOffset = AZStd::distance(cbegin(), last); + size_type count = AZStd::distance(replaceFirst, replaceLast); + size_type newSize = m_size + count - AZStd::distance(first, last); + if (fits_in_capacity(newSize)) + { + Traits::move(first + count, last, m_size - postInsertOffset); // empty out hole + for (size_t updateIndex = insertOffset; replaceFirst != replaceLast; ++replaceFirst, ++updateIndex) + { + Traits::assign(m_buffer[updateIndex], static_cast(*replaceFirst)); + } + m_size = static_cast(newSize); + Traits::assign(m_buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_fixed_string inputCopy; + for (; replaceFirst != replaceLast; ++replaceFirst) + { + inputCopy.push_back(static_cast(*replaceFirst)); + } + + return replace(first, last, inputCopy.c_str(), inputCopy.size()); + } } template inline constexpr auto basic_fixed_string::replace(const_iterator first, const_iterator last, AZStd::initializer_list ilist) -> basic_fixed_string& - { // replace [first, last) with [first2,last2) - return replace_iter(first, last, ilist.begin(), ilist.end()); + { + return replace(first, last, ilist.begin(), ilist.end()); } template @@ -1411,54 +1516,6 @@ namespace AZStd return result; } - template - template - inline constexpr auto basic_fixed_string::construct_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v> - { - // initialize from [first, last), input iterators - for (; first != last; ++first) - { - append((size_type)1, (Element)* first); - } - } - - template - template - inline constexpr auto basic_fixed_string::append_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // append [first, last), input iterators - return replace(end(), end(), first, last); - } - - template - template - inline constexpr auto basic_fixed_string::assign_iter(InputIt first, InputIt last) - -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { - return replace(begin(), end(), first, last); - } - - template - template - inline constexpr auto basic_fixed_string::insert_iter(const_iterator insertPos, InputIt first, - InputIt last) -> enable_if_t && !is_convertible_v, iterator> - { // insert [first, last) at insertPos, input iterators - difference_type offset = insertPos - cbegin(); - replace(insertPos, insertPos, first, last); - return iterator(m_buffer + offset); - } - - template - template - inline constexpr auto basic_fixed_string::replace_iter(const_iterator first, const_iterator last, - InputIt first2, InputIt last2) -> enable_if_t && !is_convertible_v, basic_fixed_string&> - { // replace [first, last) with [first2, last2), input iterators - basic_fixed_string rhs(first2, last2); - replace(first, last, rhs); - return *this; - } - template inline constexpr auto basic_fixed_string::fits_in_capacity(size_type newSize)-> bool { diff --git a/Code/Framework/AzCore/AzCore/std/string/string.h b/Code/Framework/AzCore/AzCore/std/string/string.h index e107c4657d..7dd6dd7065 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string.h +++ b/Code/Framework/AzCore/AzCore/std/string/string.h @@ -5,24 +5,43 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#ifndef AZSTD_STRING_H -#define AZSTD_STRING_H +#pragma once #include #include #include #include +#include #include #include #include #include #include -#include #include #include +namespace AZStd::StringInternal +{ + template + struct Padding + { + AZ::u8 m_padding[ElementSize - 1]; + }; + + template + struct Padding + {}; +} + +#if defined(HAVE_BENCHMARK) +namespace Benchmark +{ + class StringBenchmarkFixture; +} +#endif + namespace AZStd { /** @@ -35,130 +54,95 @@ namespace AZStd : public Debug::checked_container_base #endif { - typedef basic_string this_type; + using this_type = basic_string; public: - typedef Element* pointer; - typedef const Element* const_pointer; + using pointer = Element*; + using const_pointer = const Element*; - typedef Element& reference; - typedef const Element& const_reference; - typedef typename Allocator::difference_type difference_type; - typedef typename Allocator::size_type size_type; + using reference = Element&; + using const_reference = const Element&; + using difference_type = typename Allocator::difference_type; + using size_type = typename Allocator::size_type; - typedef pointer iterator_impl; - typedef const_pointer const_iterator_impl; + using iterator_impl = pointer; + using const_iterator_impl = const_pointer; #ifdef AZSTD_HAS_CHECKED_ITERATORS - typedef Debug::checked_randomaccess_iterator iterator; - typedef Debug::checked_randomaccess_iterator const_iterator; + using iterator = Debug::checked_randomaccess_iterator; + using const_iterator = Debug::checked_randomaccess_iterator; #else - typedef iterator_impl iterator; - typedef const_iterator_impl const_iterator; + using iterator = iterator_impl; + using const_iterator = const_iterator_impl; #endif - typedef AZStd::reverse_iterator reverse_iterator; - typedef AZStd::reverse_iterator const_reverse_iterator; - typedef Element value_type; - typedef Traits traits_type; - typedef Allocator allocator_type; + using reverse_iterator = AZStd::reverse_iterator; + using const_reverse_iterator = AZStd::reverse_iterator; + using value_type = Element; + using traits_type = Traits; + using allocator_type = Allocator; // AZSTD extension. /** * \brief Allocation node type. Common for all AZStd containers. * In vectors case we allocate always "sizeof(node_type)*capacity" block. */ - typedef value_type node_type; + using node_type = value_type; - static const size_type npos = size_type(-1); + inline static constexpr size_type npos = size_type(-1); inline basic_string(const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { - Traits::assign(m_buffer[0], Element()); + Traits::assign(m_storage.first().GetData()[0], Element()); } inline basic_string(const_pointer ptr, size_type count, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from [ptr, ptr + count) assign(ptr, count); } inline basic_string(const_pointer ptr, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from [ptr, ) assign(ptr); } inline basic_string(size_type count, Element ch, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from count * ch assign(count, ch); } - template - inline basic_string(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + template && !is_convertible_v>> + inline basic_string(InputIt first, InputIt last, const Allocator& alloc = Allocator()) + : m_storage{ skip_element_tag{}, alloc } { // construct from [first, last) - if (first == last) - { - Traits::assign(m_buffer[0], Element()); // terminate - } - else - { - construct_iter(first, last, is_integral()); - } + assign(first, last); } inline basic_string(const_pointer first, const_pointer last) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) { // construct from [first, last), const pointers - assign(&*first, last - first); + assign(first, last - first); } - //inline basic_string(const_iterator _First, const_iterator _Last) - // : m_size(0) - // , m_capacity(SSO_BUF_SIZE-1) - //{ // construct from [_First, _Last), const_iterators - // if (first != last) - // assign(&*first, last - first); - //} - inline basic_string(const this_type& rhs) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(rhs.m_allocator) + : m_storage{ skip_element_tag{}, rhs.m_storage.second() } { assign(rhs, 0, npos); } inline basic_string(this_type&& rhs) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(AZStd::move(rhs.m_allocator)) + : m_storage{ skip_element_tag{}, AZStd::move(rhs.m_storage.second()) } { assign(AZStd::forward(rhs)); } inline basic_string(const this_type& rhs, size_type rhsOffset, size_type count = npos) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) { // construct from rhs [rhsOffset, rhsOffset + count) assign(rhs, rhsOffset, count); } inline basic_string(const this_type& rhs, size_type rhsOffset, size_type count, const Allocator& alloc) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) - , m_allocator(alloc) + : m_storage{ skip_element_tag{}, alloc } { // construct from rhs [rhsOffset, rhsOffset + count) with allocator assign(rhs, rhsOffset, count); } @@ -174,7 +158,7 @@ namespace AZStd inline ~basic_string() { // destroy the string - deallocate_memory(m_data, 0, typename allocator_type::allow_memory_leaks()); + deallocate_memory(m_storage.first().GetData(), 0, typename allocator_type::allow_memory_leaks()); } operator AZStd::basic_string_view() const @@ -182,12 +166,12 @@ namespace AZStd return AZStd::basic_string_view(data(), size()); } - inline iterator begin() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer)); } - inline const_iterator begin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer)); } - inline const_iterator cbegin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer)); } - inline iterator end() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer) + m_size)); } - inline const_iterator end() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer) + m_size)); } - inline const_iterator cend() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer) + m_size)); } + inline iterator begin() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, m_storage.first().GetData())); } + inline const_iterator begin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, m_storage.first().GetData())); } + inline const_iterator cbegin() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, m_storage.first().GetData())); } + inline iterator end() { return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, (m_storage.first().GetData()) + m_storage.first().GetSize())); } + inline const_iterator end() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (m_storage.first().GetData()) + m_storage.first().GetSize())); } + inline const_iterator cend() const { return const_iterator(AZSTD_CHECKED_ITERATOR(const_iterator_impl, (m_storage.first().GetData()) + m_storage.first().GetSize())); } inline reverse_iterator rbegin() { return reverse_iterator(end()); } inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } inline const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } @@ -196,7 +180,7 @@ namespace AZStd inline const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } inline this_type& operator=(const this_type& rhs) { return assign(rhs); } - inline this_type& operator=(this_type&& rhs) { return assign(AZStd::forward(rhs)); } + inline this_type& operator=(this_type&& rhs) { return assign(AZStd::move(rhs)); } inline this_type& operator=(AZStd::basic_string_view view) { return assign(view); } inline this_type& operator=(const_pointer ptr) { return assign(ptr); } inline this_type& operator=(Element ch) { return assign(1, ch); } @@ -208,21 +192,18 @@ namespace AZStd this_type& append(const this_type& rhs, size_type rhsOffset, size_type count) { // append rhs [rhsOffset, rhsOffset + count) AZSTD_CONTAINER_ASSERT(rhs.size() >= rhsOffset, "Invalid offset!"); - size_type num = rhs.m_size - rhsOffset; - if (num < count) - { - count = num; // trim count to size - } - AZSTD_CONTAINER_ASSERT(npos - m_size > count && m_size + count >= m_size, "result is too long!"); - num = m_size + count; - if (count > 0 && grow(num)) + count = AZStd::min(count, rhs.size() - rhsOffset); + + size_type oldSize = size(); + size_type newSize = oldSize + count; + if (count > 0 && grow(newSize)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; + pointer data = m_storage.first().GetData(); + const_pointer rhsData = rhs.data(); // make room and append new stuff - Traits::copy(data + m_size /*, m_capacity - m_size*/, rhsData + rhsOffset, count); - m_size = num; - Traits::assign(data[num], Element()); // terminate + Traits::copy(data + oldSize, rhsData + rhsOffset, count); + m_storage.first().SetSize(newSize); + Traits::assign(data[newSize], Element()); // terminate } return *this; } @@ -230,20 +211,21 @@ namespace AZStd this_type& append(const_pointer ptr, size_type count) { // append [ptr, ptr + count) - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) + pointer data = m_storage.first().GetData(); + if (ptr != nullptr && ptr >= data && (data + size()) > ptr) { return append(*this, ptr - data, count); // substring } - AZSTD_CONTAINER_ASSERT(npos - m_size > count && m_size + count >= m_size, "result is too long!"); - size_type num = m_size + count; - if (count > 0 && grow(num)) + AZSTD_CONTAINER_ASSERT(npos - size() > count && size() + count >= size(), "result is too long!"); + size_type oldSize = size(); + size_type newSize = oldSize + count; + if (count > 0 && grow(newSize)) { // make room and append new stuff - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::copy(data + m_size /*, m_capacity - m_size*/, ptr, count); - m_size = num; - Traits::assign(data[num], Element()); // terminate + data = m_storage.first().GetData(); + Traits::copy(data + oldSize , ptr, count); + m_storage.first().SetSize(newSize); + Traits::assign(data[newSize], Element()); // terminate } return *this; } @@ -252,30 +234,60 @@ namespace AZStd this_type& append(size_type count, Element ch) { // append count * ch - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "result is too long"); - size_type num = m_size + count; + AZSTD_CONTAINER_ASSERT(npos - size() > count, "result is too long"); + size_type num = size() + count; if (count > 0 && grow(num)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); // make room and append new stuff using assign - if (count == 1) - { - Traits::assign(*(data + m_size), ch); - } - else - { - Traits::assign(data + m_size, count, ch); - } - m_size = num; + Traits::assign(data + size(), count, ch); + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; } - template - inline this_type& append(InputIterator first, InputIterator last) + template + inline auto append(InputIt first, InputIt last) + -> enable_if_t && !is_convertible_v, this_type&> { // append [first, last) - return append_iter(first, last, AZStd::is_integral()); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return append(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + size_type oldSize = size(); + size_type newSize = oldSize + AZStd::distance(first, last); + if (grow(newSize)) + { + pointer buffer = data(); + for (size_t updateIndex = oldSize; first != last; ++first, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*first)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } + + return append(inputCopy.c_str(), inputCopy.size()); + } } inline this_type& append(const_pointer first, const_pointer last) @@ -283,11 +295,6 @@ namespace AZStd return replace(end(), end(), first, last); } - //inline this_type& append(const_iterator first, const_iterator last) - //{ // append [first, last), const_iterators - // return replace(end(), end(), first, last); - //} - inline this_type& assign(const this_type& rhs) { return assign(rhs, 0, npos); @@ -302,27 +309,34 @@ namespace AZStd { if (this != &rhs) { - if (SSO_BUF_SIZE <= m_capacity) + deallocate_memory(m_storage.first().GetData(), 0, typename allocator_type::allow_memory_leaks()); + + m_storage.first().SetCapacity(rhs.capacity()); + + pointer data = m_storage.first().GetData(); + pointer rhsData = rhs.data(); + // Memmove the right hand side string data if it is using the short string optimization + // Otherwise set the pointer to the right hand side + if (rhs.m_storage.first().ShortStringOptimizationActive()) { - deallocate_memory(m_data, 0, typename allocator_type::allow_memory_leaks()); + Traits::move(data, rhsData, rhs.size() + 1); // string + null-terminator } + else + { + m_storage.first().SetData(rhsData); + } + m_storage.first().SetSize(rhs.size()); + m_storage.second() = rhs.m_storage.second(); - Traits::move(m_buffer, rhs.m_buffer, sizeof(m_buffer)); - m_size = rhs.m_size; - m_capacity = rhs.m_capacity; - m_allocator = rhs.m_allocator; - - rhs.m_data = nullptr; - rhs.m_size = 0; - rhs.m_capacity = SSO_BUF_SIZE - 1; + rhs.leak_and_reset(); } return *this; } this_type& assign(const this_type& rhs, size_type rhsOffset, size_type count) { // assign rhs [rhsOffset, rhsOffset + count) - AZSTD_CONTAINER_ASSERT(rhs.m_size >= rhsOffset, "Invalid offset"); - size_type num = rhs.m_size - rhsOffset; + AZSTD_CONTAINER_ASSERT(rhs.size() >= rhsOffset, "Invalid offset"); + size_type num = rhs.size() - rhsOffset; if (count < num) { num = count; // trim num to size @@ -334,10 +348,10 @@ namespace AZStd } else if (grow(num)) { // make room and assign new stuff - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - Traits::copy(data /*, m_capacity*/, rhsData + rhsOffset, num); - m_size = num; + pointer data = m_storage.first().GetData(); + const_pointer rhsData = rhs.data(); + Traits::copy(data, rhsData + rhsOffset, num); + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; @@ -345,20 +359,20 @@ namespace AZStd this_type& assign(const_pointer ptr, size_type count) { // assign [ptr, ptr + count) - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) + pointer data = m_storage.first().GetData(); + if (ptr != nullptr && ptr >= data && (data + size()) > ptr) { return assign(*this, ptr - data, count); // substring } if (grow(count)) { // make room and assign new stuff - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); if (count > 0) { Traits::copy(data, ptr, count); } - m_size = count; + m_storage.first().SetSize(count); Traits::assign(data[count], Element()); // terminate } return *this; @@ -367,109 +381,132 @@ namespace AZStd this_type& assign(size_type count, Element ch) { // assign count * ch - AZSTD_CONTAINER_ASSERT(count != npos, "result is too long!"); if (grow(count)) { // make room and assign new stuff - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (count == 1) + pointer data = m_storage.first().GetData(); + Traits::assign(data, count, ch); + m_storage.first().SetSize(count); + Traits::assign(data[count], Element()); // terminate + } + return *this; + } + + template + auto assign(InputIt first, InputIt last) + -> enable_if_t && !is_convertible_v, this_type&> + { + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + return assign(AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // forward iterator pointer type doesn't match the const_pointer type + // So the elements need to be assigned one by one into the buffer + size_type newSize = AZStd::distance(first, last); + if (grow(newSize)) { - Traits::assign(*(data), ch); + pointer buffer = data(); + for (size_t updateIndex = 0; first != last; ++first, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*first)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate } - else + return *this; + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; first != last; ++first) { - Traits::assign(data, count, ch); + inputCopy.push_back(static_cast(*first)); } - m_size = count; - Traits::assign(data[count], Element()); // terminate + + return assign(inputCopy.c_str(), inputCopy.size()); } - return *this; } - - template - inline this_type& assign(InputIterator first, InputIterator last) { return assign_iter(first, last, AZStd::is_integral()); } - inline this_type& assign(const_pointer first, const_pointer last) { return replace(begin(), end(), first, last); } - inline this_type& insert(size_type offset, const this_type& rhs) { return insert(offset, rhs, 0, npos); } + inline this_type& insert(size_type offset, const this_type& rhs) { return insert(offset, rhs, 0, npos); } this_type& insert(size_type offset, const this_type& rhs, size_type rhsOffset, size_type count) { // insert rhs [rhsOffset, rhsOffset + count) at offset - AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offset(s)"); - size_type num = rhs.m_size - rhsOffset; + AZSTD_CONTAINER_ASSERT(size() >= offset && rhs.size() >= rhsOffset, "Invalid offset(s)"); + size_type num = rhs.size() - rhsOffset; if (num < count) { count = num; // trim _Count to size } - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "Result is too long"); - num = m_size + count; + AZSTD_CONTAINER_ASSERT(npos - size() > count, "Result is too long"); + num = size() + count; if (count > 0 && grow(num)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); // make room and insert new stuff - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + offset, m_size - offset); // empty out hole + Traits::move(data + offset + count, data + offset, size() - offset); // empty out hole if (this == &rhs) { - Traits::move(data + offset /*, m_capacity - offset*/, data + (offset < rhsOffset ? rhsOffset + count : rhsOffset), count); // substring + Traits::move(data + offset, data + (offset < rhsOffset ? rhsOffset + count : rhsOffset), count); // substring } else { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - Traits::copy(data + offset /*, m_capacity - offset*/, rhsData + rhsOffset, count); // fill hole + const_pointer rhsData = rhs.data(); + Traits::copy(data + offset, rhsData + rhsOffset, count); // fill hole } - m_size = num; + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return (*this); } - this_type& insert(size_type offset, const_pointer ptr, size_type count) + this_type& insert(size_type offset, const_pointer ptr, size_type count) { // insert [ptr, ptr + count) at offset - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) + pointer data = m_storage.first().GetData(); + if (ptr != nullptr && ptr >= data && (data + size()) > ptr) { - return insert(offset, *this, ptr - data, count); // substring + return insert(offset, *this, ptr - data, count); // substring } - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "Result is too long"); - size_type num = m_size + count; + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + AZSTD_CONTAINER_ASSERT(npos - size() > count, "Result is too long"); + size_type num = size() + count; if (count > 0 && grow(num)) { // make room and insert new stuff - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + offset, m_size - offset); // empty out hole - Traits::copy(data + offset /*, m_capacity - offset*/, ptr, count); // fill hole - m_size = num; + data = m_storage.first().GetData(); + Traits::move(data + offset + count, data + offset, size() - offset); // empty out hole + Traits::copy(data + offset, ptr, count); // fill hole + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; } - inline this_type& insert(size_type offset, const_pointer ptr) { return insert(offset, ptr, Traits::length(ptr)); } + inline this_type& insert(size_type offset, const_pointer ptr) { return insert(offset, ptr, Traits::length(ptr)); } this_type& insert(size_type offset, size_type count, Element ch) { // insert count * ch at offset - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - AZSTD_CONTAINER_ASSERT(npos - m_size > count, "Result is too long"); - size_type num = m_size + count; + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + AZSTD_CONTAINER_ASSERT(npos - size() > count, "Result is too long"); + size_type num = size() + count; if (count > 0 && grow(num)) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); // make room and insert new stuff - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + offset, m_size - offset); // empty out hole - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, count, ch); - } - m_size = num; + Traits::move(data + offset + count, data + offset, size() - offset); // empty out hole + Traits::assign(data + offset, count, ch); + m_storage.first().SetSize(num); Traits::assign(data[num], Element()); // terminate } return *this; } - inline iterator insert(const_iterator insertPos) { return insert(insertPos, Element()); } + inline iterator insert(const_iterator insertPos) { return insert(insertPos, Element()); } iterator insert(const_iterator insertPos, Element ch) { @@ -479,54 +516,89 @@ namespace AZStd const_pointer insertPosPtr = insertPos; #endif - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + const_pointer data = m_storage.first().GetData(); size_type offset = insertPosPtr - data; insert(offset, 1, ch); return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, data + offset)); } - void insert(const_iterator insertPos, size_type count, Element ch) + iterator insert(const_iterator insertPos, size_type count, Element ch) { // insert count * elem at insertPos #ifdef AZSTD_HAS_CHECKED_ITERATORS const_pointer insertPosPtr = insertPos.get_iterator(); #else const_pointer insertPosPtr = insertPos; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); size_type offset = insertPosPtr - data; insert(offset, count, ch); + return begin() + offset; } - template - inline void insert(const_iterator insertPos, InputIterator first, InputIterator last) + template + auto insert(const_iterator insertPos, InputIt first, InputIt last) + -> enable_if_t && !is_convertible_v, iterator> { // insert [_First, _Last) at _Where - insert_iter(insertPos, first, last, is_integral()); - } + size_type insertOffset = AZStd::distance(cbegin(), insertPos); + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) + { + insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be inserted one by one into the buffer + size_type count = AZStd::distance(first, last); + size_type oldSize = size(); + size_type newSize = oldSize + count; + if (grow(newSize)) + { + pointer buffer = m_storage.first().GetData(); + Traits::copy_backward(buffer + insertOffset + count, buffer + insertOffset, oldSize - insertOffset); // empty out hole + for (size_t updateIndex = insertOffset; first != last; ++first, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*first)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + } + else + { + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; first != last; ++first) + { + inputCopy.push_back(static_cast(*first)); + } - inline void insert(const_iterator insertPos, const_pointer first, const_pointer last) - { // insert [first, last) at insertPos, const pointers - replace(insertPos, insertPos, first, last); + insert(insertOffset, inputCopy.c_str(), inputCopy.size()); + } + return begin() + insertOffset; } - this_type& erase(size_type offset = 0, size_type count = npos) { // erase elements [offset, offset + count) - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; // trim count + count = size() - offset; // trim count } if (count > 0) { // move elements down - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); #ifdef AZSTD_HAS_CHECKED_ITERATORS orphan_range(data + offset, data + offset + count); #endif - Traits::move(data + offset /*, m_capacity - offset*/, data + offset + count, m_size - offset - count); - m_size = m_size - count; - Traits::assign(data[m_size], Element()); // terminate - } + Traits::move(data + offset, data + offset + count, size() - offset - count); + m_storage.first().SetSize(size() - count); + Traits::assign(data[size()], Element()); // terminate + } return *this; } @@ -538,10 +610,10 @@ namespace AZStd const_pointer erasePtr = erasePos; #endif // erase element at insertPos - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + const_pointer data = m_storage.first().GetData(); size_type count = erasePtr - data; erase(count, 1); - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, data + count)); } @@ -554,159 +626,152 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); size_type count = firstPtr - data; erase(count, lastPtr - firstPtr); - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); return iterator(AZSTD_CHECKED_ITERATOR(iterator_impl, data + count)); } - inline void clear() { erase(begin(), end()); } - inline this_type& replace(size_type offset, size_type count, const this_type& rhs) + inline void clear() { erase(begin(), end()); } + this_type& replace(size_type offset, size_type count, const this_type& rhs) { - // replace [offset, offset + count) with rhs - return replace(offset, count, rhs, 0, npos); + return replace(offset, count, rhs.c_str(), rhs.size()); } this_type& replace(size_type offset, size_type count, const this_type& rhs, size_type rhsOffset, size_type rhsCount) { - // replace [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) - AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offsets"); - if (m_size - offset < count) - { - count = m_size - offset; // trim count to size - } - size_type num = rhs.m_size - rhsOffset; - if (num < rhsCount) - { - rhsCount = num; // trim rhsCount to size - } - AZSTD_CONTAINER_ASSERT(npos - rhsCount > m_size - count, "Result is too long"); - - size_type nm = m_size - count - offset; // length of preserved tail - size_type newSize = m_size + rhsCount - count; - if (m_size < newSize) - { - grow(newSize); - } - - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_range(data + offset, data + offset + count); -#endif - if (this != &rhs) - { // no overlap, just move down and copy in new stuff - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // empty hole - Traits::copy(data + offset /*, m_capacity - offset*/, rhsData + rhsOffset, rhsCount); // fill hole - } - else if (rhsCount <= count) - { // hole doesn't get larger, just copy in substring - Traits::move(data + offset /*, m_capacity - offset*/, data + rhsOffset, rhsCount); // fill hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - } - else if (rhsOffset <= offset) - { // hole gets larger, substring begins before hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - Traits::move(data + offset /*, m_capacity - offset*/, data + rhsOffset, rhsCount); // fill hole - } - else if (offset + count <= rhsOffset) - { // hole gets larger, substring begins after hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - Traits::move(data + offset /*, m_capacity - offset*/, data + (rhsOffset + rhsCount - count), rhsCount); // fill hole - } - else - { // hole gets larger, substring begins in hole - Traits::move(data + offset /*, m_capacity - offset*/, data + rhsOffset, count); // fill old hole - Traits::move(data + offset + rhsCount /*, m_capacity - offset - rhsCount*/, data + offset + count, nm); // move tail down - Traits::move(data + offset + count /*, m_capacity - offset - count*/, data + rhsOffset + rhsCount, rhsCount - count); // fill rest of new hole - } - - m_size = newSize; - Traits::assign(data[newSize], Element()); // terminate - return (*this); + return replace(offset, count, rhs.c_str() + rhsOffset, AZStd::min(rhsCount, rhs.size() - rhsOffset)); } this_type& replace(size_type offset, size_type count, const_pointer ptr, size_type ptrCount) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; // replace [offset, offset + count) with [ptr, ptr + ptrCount) - if (ptr != 0 && ptr >= data && (data + m_size) > ptr) - { - return (replace(offset, count, *this, ptr - data, ptrCount)); // substring, replace carefully - } - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) - { - count = m_size - offset; // trim _N0 to size + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + // Make sure count is within is no larger than the distance from the offset + // to the end of this string + count = AZStd::min(count, size() - offset); + + size_type newSize = size() + ptrCount - count; + size_type charsAfterCountToMove = size() - count - offset; + pointer inputStringCopy{}; + + if (pointer thisBuffer = m_storage.first().GetData(); + (ptr >= thisBuffer && ptr < thisBuffer + size()) + || (ptr + ptrCount > thisBuffer && ptr + ptrCount <= thisBuffer + size())) + { + // Overlap checks for tring needs if the input pointer is anywhere within the string + // even if it is outside of the range of [offset, offset + count) as a growing + // the string buffer could cause a realloc to occur + if (!fits_in_capacity(newSize)) + { + // If the input string is a sub-string and it would cause + // this string to need to re-allocated as it doesn't fit in the capacity + // Then the input string is needs to be copied into a local buffer + inputStringCopy = reinterpret_cast(get_allocator().allocate(ptrCount * sizeof(value_type), alignof(value_type))); + Traits::copy(inputStringCopy, ptr, ptrCount); + // Updated the input string pointer to point to the local buffer + ptr = inputStringCopy; + // Now this string buffer can now be safely resized and the non-overlapping string logic below can be used + } + else + { + // overlapping string in-place logic + // Ex. this = "ABCDEFG", offset = 1, count=4 + // substring is "CDE" + // The text from offset 1 for 4 chars "BCDE": should be replaced with "CDE" + // making a whole for the bytes results in output = "ABCDFG" + // Afterwards output = "ACDEFG" + // The input string overlaps with this string in this case + // So the string is copied piecewise + if (ptrCount <= count) + { // hole doesn't get larger, just copy in substring + Traits::move(thisBuffer + offset, ptr, ptrCount); // fill hole + Traits::copy(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + } + else + { + if (ptr <= thisBuffer + offset) + { // hole gets larger, substring begins before hole + Traits::copy_backward(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(thisBuffer + offset, ptr, ptrCount); // fill hole + } + else if (thisBuffer + offset + count <= ptr) + { // hole gets larger, substring begins after hole + Traits::copy_backward(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(thisBuffer + offset, ptr + (ptrCount - count), ptrCount); // fill hole + } + else + { // hole gets larger, substring begins in hole + Traits::copy(thisBuffer + offset, ptr, count); // fill old hole + Traits::copy_backward(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); // move tail down + Traits::copy(thisBuffer + offset + count, ptr + ptrCount, ptrCount - count); // fill rest of new hole + } + } + m_storage.first().SetSize(newSize); + Traits::assign(thisBuffer[newSize], Element()); // terminate + return *this; + } } - AZSTD_CONTAINER_ASSERT(npos - ptrCount > m_size - count, "Result too long"); -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_range(data + offset, data + offset + count); -#endif - size_type nm = m_size - count - offset; - if (ptrCount < count) - { - Traits::move(data + offset + ptrCount, data + offset + count, nm); // smaller hole, move tail up - } - size_type num = m_size + ptrCount - count; - if ((0 < ptrCount || 0 < count) && grow(num)) + // input string doesn't overlap, so this string can be re-allocated safely + if (grow(newSize)) { - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - // make room and rearrange - if (count < ptrCount) + // Need to regrab the memory address for the storage buffer + // in case the grow re-allocated memory + pointer thisBuffer = m_storage.first().GetData(); + if (count != ptrCount) { - Traits::move(data + offset + ptrCount /*, m_capacity - offset - ptrCount*/, data + offset + count, nm); // move tail down + Traits::move(thisBuffer + offset + ptrCount, thisBuffer + offset + count, charsAfterCountToMove); } if (ptrCount > 0) { - Traits::copy(data + offset /*, m_capacity - offset*/, ptr, ptrCount); // fill hole + // Copy bytes up to the minimum of this string count and input string count + Traits::copy(thisBuffer + offset, ptr, ptrCount); } + // input string doesn't overlap, so this string can be re-allocated safely + m_storage.first().SetSize(newSize); + Traits::assign(thisBuffer[newSize], Element()); // terminate + } - m_size = num; - Traits::assign(data[num], Element()); // terminate + // If a local string was allocated, then de-allocate its memory + if (inputStringCopy != nullptr) + { + get_allocator().deallocate(inputStringCopy, 0, alignof(value_type)); } + return *this; } inline this_type& replace(size_type offset, size_type count, const_pointer ptr) { return replace(offset, count, ptr, Traits::length(ptr)); } this_type& replace(size_type offset, size_type count, size_type num, Element ch) { // replace [offset, offset + count) with num * ch - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; // trim count to size + count = size() - offset; // trim count to size } - AZSTD_CONTAINER_ASSERT(npos - num > m_size - count, "Result is too long"); - size_type nm = m_size - count - offset; + AZSTD_CONTAINER_ASSERT(npos - num > size() - count, "Result is too long"); + size_type nm = size() - count - offset; - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); #ifdef AZSTD_HAS_CHECKED_ITERATORS orphan_range(data + offset, data + offset + count); #endif if (num < count) { - Traits::move(data + offset + num /*, m_capacity - offset - num*/, data + offset + count, nm); // smaller hole, move tail up + Traits::move(data + offset + num, data + offset + count, nm); // smaller hole, move tail up } - size_type numToGrow = m_size + num - count; + size_type numToGrow = size() + num - count; if ((0 < num || 0 < count) && grow(numToGrow)) { // make room and rearrange - data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + data = m_storage.first().GetData(); if (count < num) { - Traits::move(data + offset + num /*, m_capacity - offset - num*/, data + offset + count, nm); // move tail down + Traits::move(data + offset + num, data + offset + count, nm); // move tail down } - if (count == 1) - { - Traits::assign(*(data + offset), ch); - } - else - { - Traits::assign(data + offset, num, ch); - } - m_size = numToGrow; + Traits::assign(data + offset, num, ch); + m_storage.first().SetSize(numToGrow); Traits::assign(data[numToGrow], Element()); // terminate } return *this; @@ -722,7 +787,7 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, rhs); } @@ -735,7 +800,7 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, ptr, count); } @@ -748,7 +813,7 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, ptr); } @@ -761,113 +826,133 @@ namespace AZStd const_pointer firstPtr = first; const_pointer lastPtr = last; #endif - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); return replace(firstPtr - data, lastPtr - firstPtr, count, ch); } - template - inline this_type& replace(const_iterator first, const_iterator last, InputIterator first2, InputIterator last2) - { // replace [first, last) with [first2,last2) - return replace_iter(first, last, first2, last2, is_integral()); - } - - this_type& replace(const_iterator first, const_iterator last, const_pointer first2, const_pointer last2) + template + inline auto replace(const_iterator first, const_iterator last, InputIt replaceFirst, InputIt replaceLast) + -> enable_if_t && !is_convertible_v, this_type&> { -#ifdef AZSTD_HAS_CHECKED_ITERATORS - const_pointer first1 = first.get_iterator(); - const_pointer last1 = last.get_iterator(); -#else - const_pointer first1 = first; - const_pointer last1 = last; -#endif - // replace [first, last) with [first2, last2), const pointers - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (first2 == last2) + if constexpr (Internal::satisfies_contiguous_iterator_concept_v + && is_same_v::value_type, value_type>) { - erase(first1 - data, last1 - first1); + return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast)); + } + else if constexpr (Internal::is_forward_iterator_v) + { + // Input Iterator pointer type doesn't match the const_pointer type + // So the elements need to be appended one by one into the buffer + + size_type insertOffset = AZStd::distance(cbegin(), first); + size_type postInsertOffset = AZStd::distance(cbegin(), last); + size_type count = AZStd::distance(replaceFirst, replaceLast); + size_type oldSize = size(); + size_type newSize = oldSize + count - AZStd::distance(first, last); + if (grow(newSize)) + { + pointer buffer = data(); + Traits::move(first + count, last, oldSize - postInsertOffset); // empty out hole + for (size_t updateIndex = insertOffset; replaceFirst != replaceLast; ++replaceFirst, ++updateIndex) + { + Traits::assign(buffer[updateIndex], static_cast(*replaceFirst)); + } + m_storage.first().SetSize(newSize); + Traits::assign(buffer[newSize], Element()); // terminate + } + return *this; } else { - replace(first1 - data, last1 - first1, &*first2, last2 - first2); + // input iterator that aren't forward iterators can only be used in a single pass + // algorithm. Therefore AZStd::distance can't be used + // So the input is copied into a local string and then delegated + // to use the (const_pointer, size_type) overload + basic_string inputCopy; + for (; replaceFirst != replaceLast; ++replaceFirst) + { + inputCopy.push_back(static_cast(*replaceFirst)); + } + + return replace(first, last, inputCopy.c_str(), inputCopy.size()); } - return *this; } inline reference at(size_type offset) { // subscript mutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + pointer data = m_storage.first().GetData(); return data[offset]; } inline const_reference at(size_type offset) const { // subscript nonmutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + const_pointer data = m_storage.first().GetData(); return data[offset]; } inline reference operator[](size_type offset) { // subscript mutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + pointer data = m_storage.first().GetData(); return data[offset]; } inline const_reference operator[](size_type offset) const { // subscript nonmutable sequence with checking - AZSTD_CONTAINER_ASSERT(m_size > offset, "Invalid offset"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() > offset, "Invalid offset"); + const_pointer data = m_storage.first().GetData(); return data[offset]; } inline reference front() { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::front - string is empty!"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::front - string is empty!"); + pointer data = m_storage.first().GetData(); return data[0]; } inline const_reference front() const { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::front - string is empty!"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::front - string is empty!"); + const_pointer data = m_storage.first().GetData(); return data[0]; } inline reference back() { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::back - string is empty!"); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - return data[m_size - 1]; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::back - string is empty!"); + pointer data = m_storage.first().GetData(); + return data[size() - 1]; } inline const_reference back() const { - AZSTD_CONTAINER_ASSERT(m_size != 0, "AZStd::string::back - string is empty!"); - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - return data[m_size - 1]; + AZSTD_CONTAINER_ASSERT(size() != 0, "AZStd::string::back - string is empty!"); + const_pointer data = m_storage.first().GetData(); + return data[size() - 1]; } inline void push_back(Element ch) { - const_pointer end = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - end += m_size; + const_pointer end = data(); + end += size(); insert(end, ch); } - inline const_pointer c_str() const { return (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer); } - inline size_type length() const { return m_size; } - inline size_type size() const { return m_size; } - inline size_type capacity() const { return m_capacity; } + inline const_pointer c_str() const { return (data()); } + inline size_type length() const { return m_storage.first().GetSize(); } + inline size_type size() const { return m_storage.first().GetSize(); } + inline size_type capacity() const { return m_storage.first().GetCapacity(); } inline size_type max_size() const { // return maximum possible length of sequence - return AZStd::allocator_traits::max_size(m_allocator) / sizeof(value_type); + return AZStd::allocator_traits::max_size(m_storage.second()) / sizeof(value_type); } inline void resize(size_type newSize) @@ -877,58 +962,58 @@ namespace AZStd inline void resize_no_construct(size_type newSize) { - if (newSize <= m_size) + if (newSize <= size()) { erase(newSize); } else { reserve(newSize); - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - m_size = newSize; - Traits::assign(data[m_size], Element()); // terminate + pointer data = m_storage.first().GetData(); + m_storage.first().SetSize(newSize); + Traits::assign(data[newSize], Element()); // terminate } } inline void resize(size_type newSize, Element ch) { // determine new length, padding with ch elements as needed - if (newSize <= m_size) + if (newSize <= size()) { erase(newSize); } else { - append(newSize - m_size, ch); + append(newSize - size(), ch); } } void reserve(size_type newCapacity = 0) { // determine new minimum length of allocated storage - if (m_size <= newCapacity && m_capacity != newCapacity) + if (size() <= newCapacity && capacity() != newCapacity) { // change reservation - size_type size = m_size; + size_type curSize = size(); if (grow(newCapacity)) { - m_size = size; - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::assign(data[size], Element()); // terminate + m_storage.first().SetSize(curSize); + pointer data = m_storage.first().GetData(); + Traits::assign(data[curSize], Element()); // terminate } } } - inline bool empty() const { return (m_size == 0); } - size_type copy(Element* dest /*, size_type destSize */, size_type count, size_type offset = 0) const + inline bool empty() const { return size() == 0; } + size_type copy(Element* dest, size_type count, size_type offset = 0) const { // copy [offset, offset + count) to [dest, dest + count) // assume there is enough space in _Ptr - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; + count = size() - offset; } - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - Traits::copy(dest /*, destSize*/, data + offset, count); + const_pointer data = m_storage.first().GetData(); + Traits::copy(dest, data + offset, count); return count; } @@ -939,19 +1024,10 @@ namespace AZStd return; } - if (m_allocator == rhs.m_allocator) + if (m_storage.second() == rhs.m_storage.second()) { - // same allocator, swap control information -#ifdef AZSTD_HAS_CHECKED_ITERATORS - swap_all(rhs); -#endif - Element temp[SSO_BUF_SIZE]; - ::memcpy(temp, rhs.m_buffer, sizeof(m_buffer)); - ::memcpy(rhs.m_buffer, m_buffer, sizeof(m_buffer)); - ::memcpy(m_buffer, temp, sizeof(m_buffer)); - - AZStd::swap(m_size, rhs.m_size); - AZStd::swap(m_capacity, rhs.m_capacity); + // same allocator, swap storage + m_storage.first().swap(rhs.m_storage.first()); } else { @@ -980,174 +1056,76 @@ namespace AZStd inline size_type find(const this_type& rhs, size_type offset = 0) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find(rhsData, offset, rhs.size()); } size_type find(const_pointer ptr, size_type offset, size_type count) const { - AZ_Assert(ptr != NULL, "Invalid input!"); - - // look for [ptr, ptr + count) beginning at or after offset - if (count == 0 && offset <= m_size) - { - return offset; // null string always matches (if inside string) - } - size_type nm; - if (offset < m_size && count <= (nm = m_size - offset)) - { // room for match, look for it - const_pointer uptr, vptr; - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - for (nm -= count - 1, vptr = data + offset; (uptr = Traits::find(vptr, nm, *ptr)) != 0; nm -= uptr - vptr + 1, vptr = uptr + 1) - { - if (Traits::compare(uptr, ptr, count) == 0) - { - return (uptr - data); // found a match - } - } - } - - return (npos); // no match + return StringInternal::find(data(), size(), ptr, offset, count, npos); } inline size_type find(const_pointer ptr, size_type offset = 0) const { return find(ptr, offset, Traits::length(ptr)); } inline size_type find(Element ch, size_type offset = 0) const { return find((const_pointer) & ch, offset, 1); } inline size_type rfind(const this_type& rhs, size_type offset = npos) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return rfind(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return rfind(rhsData, offset, rhs.size()); } size_type rfind(const_pointer ptr, size_type offset, size_type count) const - { // look for [ptr, ptr + count) beginning before offset - if (count == 0) - { - return (offset < m_size ? offset : m_size); // null always matches - } - if (count <= m_size) - { // room for match, look for it - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const_pointer uptr = data + (offset < m_size - count ? offset : m_size - count); - for (;; --uptr) - { - if (Traits::eq(*uptr, *ptr) && Traits::compare(uptr, ptr, count) == 0) - { - return (uptr - data); // found a match - } - else if (uptr == data) - { - break; // at beginning, no more chance for match - } - } - } - - return npos; // no match + { + return StringInternal::rfind(data(), size(), ptr, offset, count, npos); } inline size_type rfind(const_pointer ptr, size_type offset = npos) const { return rfind(ptr, offset, Traits::length(ptr)); } inline size_type rfind(Element ch, size_type offset = npos) const { return rfind((const_pointer) & ch, offset, 1); } inline size_type find_first_of(const this_type& rhs, size_type offset = 0) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_first_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_first_of(rhsData, offset, rhs.size()); } size_type find_first_of(const_pointer ptr, size_type offset, size_type count) const - { // look for one of [ptr, ptr + count) at or after offset - if (0 < count && offset < m_size) - { // room for match, look for it - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const Element* const vptr = data + m_size; - for (const_pointer uptr = data + offset; uptr < vptr; ++uptr) - { - if (Traits::find(ptr, count, *uptr) != 0) - { - return uptr - data; // found a match - } - } - } - return npos; // no match + { + return StringInternal::find_first_of(data(), size(), ptr, offset, count, npos); } inline size_type find_first_of(const_pointer ptr, size_type offset = 0) const { return find_first_of(ptr, offset, Traits::length(ptr)); } inline size_type find_first_of(Element ch, size_type offset = 0) const { return find((const_pointer) & ch, offset, 1); } inline size_type find_last_of(const this_type& rhs, size_type offset = npos) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_last_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_last_of(rhsData, offset, rhs.size()); } size_type find_last_of(const_pointer ptr, size_type offset, size_type count) const - { // look for one of [ptr, ptr + count) before offset - if (0 < count && 0 < m_size) - { - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - for (const_pointer uptr = data + (offset < m_size ? offset : m_size - 1);; --uptr) - { - if (Traits::find(ptr, count, *uptr) != 0) - { - return uptr - data; // found a match - } - else if (uptr == data) - { - break; // at beginning, no more chance for match - } - } - } - - return npos; // no match + { + return StringInternal::find_last_of(data(), size(), ptr, offset, count, npos); } inline size_type find_last_of(const_pointer ptr, size_type offset = npos) const { return find_last_of(ptr, offset, Traits::length(ptr)); } inline size_type find_last_of(Element ch, size_type offset = npos) const { return rfind((const_pointer) & ch, offset, 1); } inline size_type find_first_not_of(const this_type& rhs, size_type offset = 0) const { // look for none of rhs at or after offset - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_first_not_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_first_not_of(rhsData, offset, rhs.size()); } size_type find_first_not_of(const_pointer ptr, size_type offset, size_type count) const { - // look for none of [ptr, ptr + count) at or after offset - if (offset < m_size) - { // room for match, look for it - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - const Element* const vptr = data + m_size; - for (const_pointer uptr = data + offset; uptr < vptr; ++uptr) - { - if (Traits::find(ptr, count, *uptr) == 0) - { - return uptr - data; - } - } - } - return npos; + return StringInternal::find_first_not_of(data(), size(), ptr, offset, count, npos); } inline size_type find_first_not_of(const_pointer ptr, size_type offset = 0) const { return find_first_not_of(ptr, offset, Traits::length(ptr)); } inline size_type find_first_not_of(Element ch, size_type offset = 0) const { return find_first_not_of((const_pointer) & ch, offset, 1); } inline size_type find_last_not_of(const this_type& rhs, size_type offset = npos) const { // look for none of rhs before offset - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return find_last_not_of(rhsData, offset, rhs.m_size); + const_pointer rhsData = rhs.data(); + return find_last_not_of(rhsData, offset, rhs.size()); } size_type find_last_not_of(const_pointer ptr, size_type offset, size_type count) const - { // look for none of [ptr, ptr + count) before offset - if (0 < m_size) - { - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - for (const_pointer uptr = data + (offset < m_size ? offset : m_size - 1);; --uptr) - { - if (Traits::find(ptr, count, *uptr) == 0) - { - return uptr - data; - } - else if (uptr == data) - { - break; - } - } - } - return npos; + { + return StringInternal::find_last_not_of(data(), size(), ptr, offset, count, npos); } inline size_type find_last_not_of(const_pointer ptr, size_type offset = npos) const { return find_last_not_of(ptr, offset, Traits::length(ptr)); } @@ -1161,8 +1139,8 @@ namespace AZStd inline int compare(const this_type& rhs) const { - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; - return compare(0, m_size, rhsData, rhs.m_size); + const_pointer rhsData = rhs.data(); + return compare(0, size(), rhsData, rhs.size()); } inline int compare(size_type offset, size_type count, const this_type& rhs) const @@ -1173,26 +1151,26 @@ namespace AZStd int compare(size_type offset, size_type count, const this_type& rhs, size_type rhsOffset, size_type rhsCount) const { // compare [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) - AZSTD_CONTAINER_ASSERT(rhs.m_size >= rhsOffset, "Invalid offset"); - if (rhs.m_size - rhsOffset < rhsCount) + AZSTD_CONTAINER_ASSERT(rhs.size() >= rhsOffset, "Invalid offset"); + if (rhs.size() - rhsOffset < rhsCount) { - rhsCount = rhs.m_size - rhsOffset; // trim rhsCount to size + rhsCount = rhs.size() - rhsOffset; // trim rhsCount to size } - const_pointer rhsData = SSO_BUF_SIZE <= rhs.m_capacity ? rhs.m_data : rhs.m_buffer; + const_pointer rhsData = rhs.data(); return compare(offset, count, rhsData + rhsOffset, rhsCount); } - inline int compare(const_pointer ptr) const { return compare(0, m_size, ptr, Traits::length(ptr)); } + inline int compare(const_pointer ptr) const { return compare(0, size(), ptr, Traits::length(ptr)); } inline int compare(size_type offset, size_type count, const_pointer ptr) const { return compare(offset, count, ptr, Traits::length(ptr)); } int compare(size_type offset, size_type count, const_pointer ptr, size_type ptrCount) const { // compare [offset, offset + _N0) with [_Ptr, _Ptr + _Count) - AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); - if (m_size - offset < count) + AZSTD_CONTAINER_ASSERT(size() >= offset, "Invalid offset"); + if (size() - offset < count) { - count = m_size - offset; // trim count to size + count = size() - offset; // trim count to size } - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + const_pointer data = m_storage.first().GetData(); size_type ans = Traits::compare(data + offset, ptr, count < ptrCount ? count : ptrCount); return (ans != 0 ? (int)ans : count < ptrCount ? -1 : count == ptrCount ? 0 : +1); } @@ -1231,11 +1209,11 @@ namespace AZStd inline void pop_back() { - if (m_size > 0) + if (!empty()) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - --m_size; - Traits::assign(data[m_size], Element()); // terminate + pointer data = m_storage.first().GetData(); + m_storage.first().SetSize(m_storage.first().GetSize() - 1); + Traits::assign(data[size()], Element()); // terminate } } @@ -1245,39 +1223,35 @@ namespace AZStd * @{ */ /// TR1 Extension. Return pointer to the vector data. The vector data is guaranteed to be stored as an array. - inline pointer data() { return (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer); } - inline const_pointer data() const { return (SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer); } + inline pointer data() { return m_storage.first().GetData(); } + inline const_pointer data() const { return m_storage.first().GetData(); } /// /// The only difference from the standard is that we return the allocator instance, not a copy. - inline allocator_type& get_allocator() { return m_allocator; } - inline const allocator_type& get_allocator() const { return m_allocator; } + inline allocator_type& get_allocator() { return m_storage.second(); } + inline const allocator_type& get_allocator() const { return m_storage.second(); } /// Set the vector allocator. If different than then current all elements will be reallocated. void set_allocator(const allocator_type& allocator) { - if (m_allocator != allocator) + if (m_storage.second() != allocator) { - if (m_size > 0 && SSO_BUF_SIZE <= m_capacity) + if (!empty() && !m_storage.first().ShortStringOptimizationActive()) { allocator_type newAllocator = allocator; - pointer data = m_data; + pointer data = m_storage.first().GetData(); - pointer newData = reinterpret_cast(newAllocator.allocate(sizeof(node_type) * (m_capacity + 1), alignment_of::value)); + pointer newData = reinterpret_cast(newAllocator.allocate(sizeof(node_type) * (capacity() + 1), alignof(node_type))); - Traits::copy(newData, data, m_size + 1); // copy elements and terminator + Traits::copy(newData, data, size() + 1); // copy elements and terminator // Free memory (if needed). deallocate_memory(data, 0, typename allocator_type::allow_memory_leaks()); - m_allocator = newAllocator; - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_all(); -#endif + m_storage.second() = newAllocator; } else { - m_allocator = allocator; + m_storage.second() = allocator; } } } @@ -1296,12 +1270,12 @@ namespace AZStd #else pointer iterPtr = iter; #endif - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (iterPtr < data || iterPtr > (data + m_size)) + const_pointer data = m_storage.first().GetData(); + if (iterPtr < data || iterPtr > (data + size())) { return isf_none; } - else if (iterPtr == (data + m_size)) + else if (iterPtr == (data + size())) { return isf_valid; } @@ -1316,12 +1290,12 @@ namespace AZStd #else const_pointer iterPtr = iter; #endif - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - if (iterPtr < data || iterPtr > (data + m_size)) + const_pointer data = m_storage.first().GetData(); + if (iterPtr < data || iterPtr > (data + size())) { return isf_none; } - else if (iterPtr == (data + m_size)) + else if (iterPtr == (data + size())) { return isf_valid; } @@ -1337,86 +1311,74 @@ namespace AZStd * \note This function is added to the vector for consistency. In the vector case we have only one allocation, and if the allocator allows memory leaks * it can just leave deallocate function empty, which performance wise will be the same. For more complex containers this will make big difference. */ - void leak_and_reset() + void leak_and_reset() { - m_size = 0; - m_capacity = SSO_BUF_SIZE - 1; - Traits::assign(m_buffer[0], Element()); - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - orphan_all(); -#endif + m_storage.first() = {}; } /** * Set the capacity, if necessary it will erase elements at the end of the container to match the new capacity. */ - void set_capacity(size_type numElements) + void set_capacity(size_type numElements) { // sets the new capacity of the vector, can be smaller than size() - if (m_capacity != numElements) + if (capacity() != numElements) { - if (numElements < SSO_BUF_SIZE) + if (numElements < ShortStringData::Capacity) { - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { // copy any leftovers to small buffer and deallocate - pointer ptr = m_data; - numElements = numElements < m_size ? numElements : m_size; + pointer ptr = m_storage.first().GetData(); + numElements = numElements < size() ? numElements : size(); + m_storage.first().SetCapacity(ShortStringData::Capacity); if (0 < numElements) { - Traits::copy(m_buffer /*, SSO_BUF_SIZE*/, ptr, numElements); + Traits::copy(m_storage.first().GetData(), ptr, numElements); } - deallocate_memory(ptr, 0, typename allocator_type::allow_memory_leaks()); - m_capacity = SSO_BUF_SIZE - 1; + // deallocate_memory functione examines the current + // m_storage short string optimization state was changed to true + // by the SetCapacity call above. Therefore m_storage.second().deallocate + // is used directly + m_storage.second().deallocate(ptr, 0, alignof(node_type)); } - m_size = numElements; - Traits::assign(m_buffer[numElements], Element()); // terminate + m_storage.first().SetSize(numElements); + Traits::assign(m_storage.first().GetData()[numElements], Element()); // terminate } else { size_type expandedSize = 0; - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { - expandedSize = m_allocator.resize(m_data, sizeof(node_type) * (numElements + 1)); + expandedSize = m_storage.second().resize(m_storage.first().GetData(), sizeof(node_type) * (numElements + 1)); // our memory managers allocate on 8+ bytes boundary and our node type should be less than that in general, otherwise // we need to take care when we compute the size on deallocate. AZ_Assert(expandedSize % sizeof(node_type) == 0, "Expanded size not a multiply of node type. This should not happen"); size_type expandedCapacity = expandedSize / sizeof(node_type); if (expandedCapacity > numElements) { - m_capacity = expandedCapacity - 1; + m_storage.first().SetCapacity(expandedCapacity - 1); return; } } - pointer newData = reinterpret_cast(m_allocator.allocate(sizeof(node_type) * (numElements + 1), alignment_of::value)); - AZSTD_CONTAINER_ASSERT(newData != 0, "AZStd::string allocation failed!"); + pointer newData = reinterpret_cast(m_storage.second().allocate(sizeof(node_type) * (numElements + 1), alignof(node_type))); + AZSTD_CONTAINER_ASSERT(newData != nullptr, "AZStd::string allocation failed!"); - size_type newSize = numElements < m_size ? numElements : m_size; - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + size_type newSize = numElements < m_storage.first().GetSize() ? numElements : m_storage.first().GetSize(); + pointer data = m_storage.first().GetData(); if (newSize > 0) { - Traits::copy(newData /*, newSize + 1*/, data, newSize); // copy existing elements - } - if (m_capacity >= SSO_BUF_SIZE) - { - deallocate_memory(m_data, expandedSize, typename allocator_type::allow_memory_leaks()); + Traits::copy(newData, data, newSize); // copy existing elements } + deallocate_memory(data, expandedSize, typename allocator_type::allow_memory_leaks()); - m_data = newData; - m_capacity = numElements; - m_size = newSize; - Traits::assign(m_data[newSize], Element()); // terminate + Traits::assign(newData[newSize], Element()); // terminate + m_storage.first().SetCapacity(numElements); + m_storage.first().SetData(newData); + m_storage.first().SetSize(newSize); } - -#ifdef AZSTD_HAS_CHECKED_ITERATORS - // when we move data in the buffer we don't really need to make invalid all iterators, but it's - // very important that we are consistent, so people don't have different behavior when they have - // short strings - orphan_all(); -#endif } } @@ -1521,9 +1483,9 @@ namespace AZStd } }; -// Clang supports compile-time check for printf-like signatures -// On MSVC, *only* if /analyze flag is enabled(defines _PREFAST_) we can also do a compile-time check -// For not affecting final release binary size, we don't use the templated version on Release configuration either + // Clang supports compile-time check for printf-like signatures + // On MSVC, *only* if /analyze flag is enabled(defines _PREFAST_) we can also do a compile-time check + // For not affecting final release binary size, we don't use the templated version on Release configuration either #if AZ_COMPILER_CLANG || defined(_PREFAST_) || defined(_RELEASE) # if AZ_COMPILER_CLANG # define FORMAT_FUNC __attribute__((format(printf, 1, 2))) @@ -1597,137 +1559,70 @@ namespace AZStd template inline basic_string(const basic_string& rhs) - : m_size(0) - , m_capacity(SSO_BUF_SIZE - 1) { assign(rhs.c_str()); } template inline this_type& operator=(const basic_string& rhs) { return assign(rhs.c_str()); } template - inline this_type& append(const basic_string& rhs) { return append(rhs.c_str()); } + inline this_type& append(const basic_string& rhs) { return append(rhs.c_str()); } template inline this_type& insert(size_type offset, const basic_string& rhs) { return insert(offset, rhs.c_str()); } template inline this_type& replace(size_type offset, size_type count, const basic_string& rhs) { return replace(offset, count, rhs.c_str()); } template - inline int compare(const basic_string& rhs) { return compare(rhs.c_str()); } + inline int compare(const basic_string& rhs) { return compare(rhs.c_str()); } // @} protected: - enum - { // length of internal buffer, [1, 16] - SSO_BUF_SIZE = 16 / sizeof (Element) < 1 ? 1 : 16 / sizeof(Element) - }; enum { // roundup mask for allocated buffers, [0, 15] - _ALLOC_MASK = sizeof (Element) <= 1 ? 15 : sizeof (Element) <= 2 ? 7 : sizeof (Element) <= 4 ? 3 : sizeof (Element) <= 8 ? 1 : 0 + _ALLOC_MASK = sizeof(Element) <= 1 ? 15 + : sizeof(Element) <= 2 ? 7 + : sizeof(Element) <= 4 ? 3 + : sizeof(Element) <= 8 ? 1 : 0 }; - template - inline this_type& append_iter(InputIterator count, InputIterator ch, const true_type& /* is_integral */) - { // append count * ch - return append((size_type)count, (Element)ch); - } - - template - inline void construct_iter(InputIterator count, InputIterator ch, const true_type& /* is_integral */) - { // initialize from count * ch - assign((size_type)count, (Element)ch); - } - - template - inline void construct_iter(InputIterator first, InputIterator last, const false_type& /*, const input_iterator_tag&*/) - { - // initialize from [first, last), input iterators - // \todo use insert ? - for (; first != last; ++first) - { - append((size_type)1, (Element) * first); - } - } - - - template - inline this_type& append_iter(InputIterator first, InputIterator last, const false_type& /* !is_integral */) - { // append [first, last), input iterators - return replace(end(), end(), first, last); - } - - - template - inline this_type& assign_iter(InputIterator count, InputIterator ch, const true_type&) { return assign((size_type)count, (Element)ch); } - template - inline this_type& assign_iter(InputIterator first, InputIterator last, const false_type&){ return replace(begin(), end(), first, last); } - - template - inline void insert_iter(const_iterator insertPos, InputIterator count, InputIterator ch, const true_type& /* is_integral() */) - { // insert count * ch at insertPos - insert(insertPos, (size_type)count, (Element)ch); - } - - template - inline void insert_iter(const_iterator insertPos, InputIterator first, InputIterator last, const false_type& /* is_integral() */) - { // insert [first, last) at insertPos, input iterators - replace(insertPos, insertPos, first, last); - } - - - template - inline this_type& replace_iter(const_iterator first, const_iterator last, InputIterator count, InputIterator ch, const true_type& /* is_intergral */) - { // replace [first, last) with count * ch - return replace(first, last, (size_type)count, (Element)ch); - } - - template - inline this_type& replace_iter(const_iterator first, const_iterator last, InputIterator first2, InputIterator last2, const false_type& /* !is_intergral */) - { // replace [first, last) with [first2, last2), input iterators - this_type rhs(first2, last2); - replace(first, last, rhs); - return *this; - } - void copy(size_type newSize, size_type oldLength) { size_type newCapacity = newSize | _ALLOC_MASK; - if (newCapacity / 3 < m_capacity / 2) + size_type currentCapacity = capacity(); + if (newCapacity / 3 < currentCapacity / 2) { - newCapacity = m_capacity + m_capacity / 2; // grow exponentially if possible + newCapacity = currentCapacity + currentCapacity / 2; // grow exponentially if possible } - if (newCapacity >= SSO_BUF_SIZE) + if (newCapacity >= ShortStringData::Capacity) { size_type expandedSize = 0; - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { - expandedSize = m_allocator.resize(m_data, sizeof(node_type) * (newCapacity + 1)); + expandedSize = m_storage.second().resize(m_storage.first().GetData(), sizeof(node_type) * (newCapacity + 1)); // our memory managers allocate on 8+ bytes boundary and our node type should be less than that in general, otherwise // we need to take care when we compute the size on deallocate. - AZ_Assert(expandedSize % sizeof(node_type) == 0, "Expanded size not a multiply of node type. This should not happen"); + AZ_Assert(expandedSize % sizeof(node_type) == 0, "Expanded size not a multiple of node type. This should not happen"); size_type expandedCapacity = expandedSize / sizeof(node_type); if (expandedCapacity > newCapacity) { - m_capacity = expandedCapacity - 1; + m_storage.first().SetCapacity(expandedCapacity - 1); return; } } - pointer newData = reinterpret_cast(m_allocator.allocate(sizeof(node_type) * (newCapacity + 1), alignment_of::value)); - AZSTD_CONTAINER_ASSERT(newData != 0, "AZStd::string allocation failed!"); + pointer newData = reinterpret_cast(m_storage.second().allocate(sizeof(node_type) * (newCapacity + 1), alignof(node_type))); + AZSTD_CONTAINER_ASSERT(newData != nullptr, "AZStd::string allocation failed!"); if (newData) { - const_pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; + pointer data = m_storage.first().GetData(); if (0 < oldLength) { - Traits::copy(newData /*, newSize + 1*/, data, oldLength); // copy existing elements - } - if (m_capacity >= SSO_BUF_SIZE) - { - deallocate_memory(m_data, expandedSize, typename allocator_type::allow_memory_leaks()); + Traits::copy(newData, data, oldLength); // copy existing elements } + deallocate_memory(data, expandedSize, typename allocator_type::allow_memory_leaks()); - m_data = newData; - m_capacity = newCapacity; - Traits::assign(m_data[newSize], Element()); // terminate + Traits::assign(newData[oldLength], Element()); // terminate + m_storage.first().SetCapacity(newCapacity); + m_storage.first().SetSize(oldLength); + m_storage.first().SetData(newData); } } } @@ -1735,40 +1630,209 @@ namespace AZStd bool grow(size_type newSize) { // ensure buffer is big enough, trim to size if _Trim is true - if (m_capacity < newSize) + if (capacity() < newSize) { - copy(newSize, m_size); // reallocate to grow + copy(newSize, size()); // reallocate to grow } else if (newSize == 0) { - pointer data = SSO_BUF_SIZE <= m_capacity ? m_data : m_buffer; - m_size = 0; + pointer data = m_storage.first().GetData(); + m_storage.first().SetSize(0); Traits::assign(data[0], Element()); // terminate } return (0 < newSize); // return true only if more work to do } + bool fits_in_capacity(size_type newSize) + { + return newSize <= capacity(); + } + inline void deallocate_memory(pointer, size_type, const true_type& /* allocator::allow_memory_leaks */) {} inline void deallocate_memory(pointer data, size_type expandedSize, const false_type& /* !allocator::allow_memory_leaks */) { - if (m_capacity >= SSO_BUF_SIZE) + if (!m_storage.first().ShortStringOptimizationActive()) { - size_type byteSize = (expandedSize == 0) ? (sizeof(node_type) * (m_capacity + 1)) : expandedSize; - m_allocator.deallocate(data, byteSize, alignment_of::value); + size_type byteSize = (expandedSize == 0) ? (sizeof(node_type) * (m_storage.first().GetCapacity() + 1)) : expandedSize; + m_storage.second().deallocate(data, byteSize, alignof(node_type)); } } - union //Storage + //! Assuming 64-bit for pointer and size_t size + //! The offset and sizes of each structure are marked below + + //! dynamically allocated data + struct AllocatedStringData { - Element m_buffer[SSO_BUF_SIZE]; //< small buffer used for small string optimization - pointer m_data; //< dynamically allocated data + AllocatedStringData() + { + m_capacity = 0; + m_ssoActive = false; + } + // bit offset: 0, bits: 64 + pointer m_data{}; + + // bit offset: 64, bit: 64 + size_type m_size{}; + + // Use all but the top bit of a size_t for the string capacity + // This allows the short string optimization to be used + // with no additional space at the cost of cutting the max_size in half + // to 2^63-1 + // offset: 128, bits: 63 + size_type m_capacity : AZStd::numeric_limits::digits - 1; + + // bit offset: 191, bits: 1 + size_type m_ssoActive : 1; + + // Total size 192 bits(24 bytes) }; - size_type m_size; // current length of string - size_type m_capacity; // current storage reserved for string - allocator_type m_allocator; + static_assert(sizeof(AllocatedStringData) <= 24, "The AllocatedStringData structure" + " should be an 8-byte pointer, 8 byte size, 63-bit capacity and 1-bit SSO flag for" + " a total of 24 bytes"); + + //! small buffer used for small string optimization + struct ShortStringData + { + //! The size can be stored within 7 bits since the buffer will be no larger + //! than 23 bytes(22 characters + 1 null-terminating character) + inline static constexpr size_type BufferMaxSize = sizeof(AllocatedStringData) - sizeof(AZ::u8); + static_assert(sizeof(Element) < BufferMaxSize, "The size of Element type must be less than the size of " + " the AllocatedStringData struct in order to use it with the basic_string class"); + inline static constexpr size_type BufferCapacityPlusNull = BufferMaxSize / sizeof(Element); + + inline static constexpr size_type Capacity = BufferCapacityPlusNull - 1; + + ShortStringData() + { + // Make sure the short string buffer is null-terminated + m_buffer[0] = Element{}; + m_size = 0; + m_ssoActive = true; + } + + // bit offset: 0, bits: 184 + Element m_buffer[BufferCapacityPlusNull]; + + // Padding to make sure for Element types with a size >1 + // such as wchar_t, that the `m_size` member starts at the bit 164 + // NOTE: Uses the anonymous struct extension + // supported by MSVC, Clang and GCC + // Takes advantage of the empty base optimization + // to have the StringInternal::Padding struct + // take 0 bytes when the Element type is 1-byte type like `char` + // When C++20 support is added, this can be changed to use [[no_unique_address]] + struct + : StringInternal::Padding + { + + // bit offset: 184, bits: 7 + AZ::u8 m_size : AZStd::numeric_limits::digits - 1; + + // bit offset: 191, bits: 1 + AZ::u8 m_ssoActive : 1; + }; + // Total size 192 bits(24 bytes) + }; + + struct PointerAlignedData + { + uintptr_t m_alignedValues[sizeof(ShortStringData) / sizeof(uintptr_t)]; + }; + + static_assert(sizeof(AllocatedStringData) == sizeof(ShortStringData) && "Short string struct must be the same size" + " as the regular allocated string struct"); + + static_assert(sizeof(PointerAlignedData) == sizeof(ShortStringData) && "Pointer aligned struct must be the same size" + " as the short string struct "); + + // The top-bit in the last byte of the AllocatedStringData and ShortStringData is used to determine if the short string optimization is being used + union Storage + { + Storage() {}; + + bool ShortStringOptimizationActive() const + { + return m_shortData.m_ssoActive; + } + const_pointer GetData() const + { + return ShortStringOptimizationActive() ? m_shortData.m_buffer + : reinterpret_cast(m_shortData).m_data; + } + pointer GetData() + { + return ShortStringOptimizationActive() ? m_shortData.m_buffer + : reinterpret_cast(m_shortData).m_data; + } + void SetData(pointer address) + { + if (!ShortStringOptimizationActive()) + { + reinterpret_cast(m_shortData).m_data = address; + } + else + { + AZSTD_CONTAINER_ASSERT(false, "Programming Error: string class is invoking SetData when the Short Optimization" + " is active. Make sure SetCapacity() is invoked" + " before calling this function."); + } + } + size_type GetSize() const + { + return ShortStringOptimizationActive() ? m_shortData.m_size + : reinterpret_cast(m_shortData).m_size; + } + void SetSize(size_type size) + { + if (ShortStringOptimizationActive()) + { + m_shortData.m_size = size; + } + else + { + reinterpret_cast(m_shortData).m_size = size; + } + } + size_type GetCapacity() const + { + return ShortStringOptimizationActive() ? m_shortData.Capacity + : reinterpret_cast(m_shortData).m_capacity; + } + void SetCapacity(size_type capacity) + { + if (capacity <= ShortStringData::Capacity) + { + m_shortData.m_ssoActive = true; + } + else + { + m_shortData.m_ssoActive = false; + reinterpret_cast(m_shortData).m_capacity = capacity; + } + } + void swap(Storage& rhs) + { + // Use pointer sized swaps to swap the string storage + AZStd::aligned_storage_for_t tempStorage; + ::memcpy(&tempStorage, this, sizeof(Storage)); + ::memcpy(this, &rhs, sizeof(Storage)); + ::memcpy(&rhs, &tempStorage, sizeof(Storage)); + } + private: + ShortStringData m_shortData{}; + AllocatedStringData m_allocatedData; + PointerAlignedData m_pointerData; + }; + + AZStd::compressed_pair m_storage; + +#if defined(HAVE_BENCHMARK) + friend class Benchmark::StringBenchmarkFixture; +#endif #ifdef AZSTD_HAS_CHECKED_ITERATORS void orphan_range(pointer first, pointer last) const @@ -1809,18 +1873,7 @@ namespace AZStd }; template - const typename basic_string::size_type basic_string::npos; - - // basic_string implements a performant swap - /*template - class move_operation_category > - { - public: - typedef swap_move_tag move_cat; - };*/ - - template - inline void swap(basic_string& left, basic_string& right) + inline void swap(basic_string& left, basic_string& right) { left.swap(right); } @@ -2027,6 +2080,3 @@ namespace AZStd }; } // namespace AZStd - -#endif // AZSTD_STRING_H -#pragma once diff --git a/Code/Framework/AzCore/AzCore/std/string/string_view.h b/Code/Framework/AzCore/AzCore/std/string/string_view.h index 30e61f95ce..841044dd16 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string_view.h +++ b/Code/Framework/AzCore/AzCore/std/string/string_view.h @@ -280,18 +280,36 @@ namespace AZStd static constexpr char_type* assign(char_type* dest, size_t count, char_type ch) noexcept { AZ_Assert(dest, "Invalid input!"); - for (char_type* iter = dest; count; --count, ++iter) + + if constexpr (AZStd::is_same_v) { - assign(*iter, ch); + // Use builtin_memset if available for char type + if (az_builtin_is_constant_evaluated()) + { + for (char_type* iter = dest; count; --count, ++iter) + { + assign(*iter, ch); + } + } + else + { + ::memset(dest, ch, count); + } + } + else + { + for (char_type* iter = dest; count; --count, ++iter) + { + assign(*iter, ch); + } } + return dest; } static constexpr bool eq(char_type left, char_type right) noexcept { return left == right; } static constexpr bool lt(char_type left, char_type right) noexcept { return left < right; } static constexpr int compare(const char_type* s1, const char_type* s2, size_t count) noexcept { - // Regression in VS2017 15.8 and 15.9 where __builtin_memcmp fails in valid checks in constexpr evaluation -#if !defined(AZ_COMPILER_MSVC) || AZ_COMPILER_MSVC < 1915 || AZ_COMPILER_MSVC > 1916 if constexpr (AZStd::is_same_v) { return __builtin_memcmp(s1, s2, count); @@ -301,7 +319,6 @@ namespace AZStd return __builtin_wmemcmp(s1, s2, count); } else -#endif { for (; count; --count, ++s1, ++s2) { @@ -339,10 +356,6 @@ namespace AZStd } static constexpr const char_type* find(const char_type* s, size_t count, const char_type& ch) noexcept { - // There is a bug with the __builtin_char_memchr intrinsic in Visual Studio 2017 15.8.x and 15.9.x - // It reads in one more additional character than the value of count. - // This is probably due to assuming null-termination -#if !defined(AZ_COMPILER_MSVC) || AZ_COMPILER_MSVC < 1915 || AZ_COMPILER_MSVC > 1916 if constexpr (AZStd::is_same_v) { return __builtin_char_memchr(s, ch, count); @@ -353,7 +366,6 @@ namespace AZStd } else -#endif { for (; count; --count, ++s) { @@ -368,64 +380,112 @@ namespace AZStd static constexpr char_type* move(char_type* dest, const char_type* src, size_t count) noexcept { AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!"); - if (count == 0) + if (count == 0 || src == dest) { return dest; } - char_type* result = dest; - // The less than(<), greater than(>) and other variants(<=, >=) - // Cannot be compare pointers within a constexpr due to the potential for undefined behavior - // per the bullet linked in the C++ standard at http://eel.is/c++draft/expr.compound#expr.rel-5 - // Now clang and gcc compilers allow the use of this relation operators in a constexpr, but - // msvc is not so forgiving - // So a workaround of iterating the src pointer, checking for equality with the dest pointer - // is used to check for overlap - auto should_copy_forward = [](const char_type* dest1, const char_type* src2, size_t count2) constexpr -> bool - { - bool dest_less_than_src{ true }; - for(const char_type* src_iter = src2; src_iter != src2 + count2; ++src_iter) + + #if az_has_builtin_memmove + __builtin_memmove(dest, src, count * sizeof(char_type)); + #else + auto NonBuiltinMove = [](char_type* dest1, const char_type* src1, size_t count1) constexpr + -> char_type* + { + if (az_builtin_is_constant_evaluated()) { - if (src_iter == dest1) + // The less than(<), greater than(>) and other variants(<=, >=) + // Cannot be compare pointers within a constexpr due to the potential for undefined behavior + // per the bullet linked in the C++ standard at http://eel.is/c++draft/expr.compound#expr.rel-5 + // Now clang and gcc compilers allow the use of this relation operators in a constexpr, but + // msvc is not so forgiving + // So a workaround of iterating the src pointer, checking for equality with the dest pointer + // is used to check for overlap + auto should_copy_forward = [](const char_type* dest2, const char_type* src2, size_t count2) constexpr -> bool + { + bool dest_less_than_src{ true }; + for (const char_type* src_iter = src2; src_iter != src2 + count2; ++src_iter) + { + if (src_iter == dest2) + { + dest_less_than_src = false; + break; + } + } + return dest_less_than_src; + }; + + if (should_copy_forward(dest1, src1, count1)) { - dest_less_than_src = false; - break; + copy(dest1, src1, count1); + } + else + { + copy_backward(dest1, src1, count1); } } - return dest_less_than_src; - }; + else + { + // Use the faster ::memmove operation at runtime + ::memmove(dest1, src1, count1 * sizeof(char_type)); + } - if (should_copy_forward(dest, src, count)) - { - copy(dest, src, count); - } - else - { - copy_backward(dest, src, count); - } + return dest1; + }; + NonBuiltinMove(dest, src, count); + #endif - return result; + return dest; } static constexpr char_type* copy(char_type* dest, const char_type* src, size_t count) noexcept { AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!"); - char_type* result = dest; - for(; count; --count, ++dest, ++src) + + #if az_has_builtin_memcpy + __builtin_memcpy(dest, src, count * sizeof(char_type)); + #else + auto NonBuiltinCopy = [](char_type* dest1, const char_type* src1, size_t count1) constexpr + -> char_type* { - assign(*dest, *src); - } - return result; + if (az_builtin_is_constant_evaluated()) + { + for (; count1; --count1, ++dest1, ++src1) + { + assign(*dest1, *src1); + } + } + else + { + ::memcpy(dest1, src1, count1 * sizeof(char_type)); + } + return dest1; + }; + NonBuiltinCopy(dest, src, count); + #endif + + return dest; } // Extension for constexpr workarounds: Addresses of a string literal cannot be compared at compile time and MSVC and clang will just refuse to compile the constexpr // Adding a copy_backwards overload that always copies backwards. - static constexpr char_type* copy_backward(char_type* dest, const char_type*src, size_t count) noexcept + static constexpr char_type* copy_backward(char_type* dest, const char_type* src, size_t count) noexcept { char_type* result = dest; - dest += count; - src += count; - for (; count; --count) + #if az_has_builtin_memmove + __builtin_memmove(dest, src, count * sizeof(char_type)); + #else + if (az_builtin_is_constant_evaluated()) + { + dest += count; + src += count; + for (; count; --count) + { + assign(*--dest, *--src); + } + } + else { - assign(*--dest, *--src); + ::memmove(dest, src, count); } + #endif return result; } diff --git a/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis b/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis index 3730b8a4f9..dca82e2439 100644 --- a/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis +++ b/Code/Framework/AzCore/Platform/Common/VisualStudio/AzCore/Natvis/azcore.natvis @@ -10,6 +10,13 @@ + + {m_element} + + + {$T1} is empty + + reverse_iterator base() {m_current} @@ -388,35 +395,41 @@ - - {m_buffer,s} - {m_data,s} - m_buffer,s - m_data,s + + {((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,s} + {((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,s} + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,s + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,s - m_size - m_capacity + (size_t)((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.Capacity + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_capacity - m_size - m_buffer - m_data + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size,u + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer + ((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data - {m_buffer,su} - {m_data,su} - m_buffer,su - m_data,su - - m_size - m_capacity + {((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,su} + {((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,su} + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,su + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,su + + (size_t)((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.Capacity + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_capacity - m_size - m_buffer - m_data + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size,u + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer + ((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data diff --git a/Code/Framework/AzCore/Tests/AZStd/String.cpp b/Code/Framework/AzCore/Tests/AZStd/String.cpp index 356310a94d..0f84ad0970 100644 --- a/Code/Framework/AzCore/Tests/AZStd/String.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/String.cpp @@ -8,11 +8,11 @@ #include "UserTypes.h" #include -#include #include #include #include #include +#include #include #include #include @@ -21,13 +21,10 @@ #include // we need this for AZ_TEST_FLOAT compare -#include #include #include #include -using namespace AZStd; - // Because of the SSO (small string optimization) we always shoule have capacity != 0 and data != 0 #define AZ_TEST_VALIDATE_EMPTY_STRING(_String) \ EXPECT_TRUE(_String.validate()); \ @@ -81,8 +78,6 @@ namespace UnitTest va_end(mark); } -#if !AZ_UNIT_TEST_SKIP_STD_STRING_TESTS - TEST(StringC, VSNPrintf) { char buffer32[32]; @@ -168,75 +163,75 @@ namespace UnitTest { const char* sChar = "SSO string"; // 10 characters const char* sCharLong = "This is a long string test that will allocate"; // 45 characters - array aChar = { + AZStd::array aChar = { { 'a', 'b', 'c', 'd', 'e', 'f' } }; // short string (should use SSO) - string str1; + AZStd::string str1; AZ_TEST_VALIDATE_EMPTY_STRING(str1); // short char* - string str2(sChar); + AZStd::string str2(sChar); AZ_TEST_VALIDATE_STRING(str2, 10); - string str2_1(""); + AZStd::string str2_1(""); AZ_TEST_VALIDATE_EMPTY_STRING(str2_1); - string str3(sChar, 5); + AZStd::string str3(sChar, 5); AZ_TEST_VALIDATE_STRING(str3, 5); // long char* - string str4(sCharLong); + AZStd::string str4(sCharLong); AZ_TEST_VALIDATE_STRING(str4, 45); - string str5(sCharLong, 35); + AZStd::string str5(sCharLong, 35); AZ_TEST_VALIDATE_STRING(str5, 35); // element - string str6(13, 'a'); + AZStd::string str6(13, 'a'); AZ_TEST_VALIDATE_STRING(str6, 13); - string str6_1(0, 'a'); + AZStd::string str6_1(0, 'a'); AZ_TEST_VALIDATE_EMPTY_STRING(str6_1); - string str7(aChar.begin(), aChar.end()); + AZStd::string str7(aChar.begin(), aChar.end()); AZ_TEST_VALIDATE_STRING(str7, 6); - string str7_1(aChar.begin(), aChar.begin()); + AZStd::string str7_1(aChar.begin(), aChar.begin()); AZ_TEST_VALIDATE_EMPTY_STRING(str7_1); - string str8(sChar, sChar + 3); + AZStd::string str8(sChar, sChar + 3); AZ_TEST_VALIDATE_STRING(str8, 3); - string str8_1(sChar, sChar); + AZStd::string str8_1(sChar, sChar); AZ_TEST_VALIDATE_EMPTY_STRING(str8_1); // - string str9(str2); + AZStd::string str9(str2); AZ_TEST_VALIDATE_STRING(str9, 10); - string str9_1(str1); + AZStd::string str9_1(str1); AZ_TEST_VALIDATE_EMPTY_STRING(str9_1); - string str10(str2, 4); + AZStd::string str10(str2, 4); AZ_TEST_VALIDATE_STRING(str10, 6); - string str11(str2, 4, 3); + AZStd::string str11(str2, 4, 3); AZ_TEST_VALIDATE_STRING(str11, 3); - string str12(sChar); - string large = sCharLong; + AZStd::string str12(sChar); + AZStd::string large = sCharLong; // move ctor - string strSm = AZStd::move(str12); + AZStd::string strSm = AZStd::move(str12); AZ_TEST_VALIDATE_STRING(strSm, 10); AZ_TEST_VALIDATE_EMPTY_STRING(str12); - string strLg(AZStd::move(large)); + AZStd::string strLg(AZStd::move(large)); AZ_TEST_VALIDATE_STRING(strLg, 45); AZ_TEST_VALIDATE_EMPTY_STRING(large); - string strEmpty(AZStd::move(str1)); + AZStd::string strEmpty(AZStd::move(str1)); AZ_TEST_VALIDATE_EMPTY_STRING(strEmpty); AZ_TEST_VALIDATE_EMPTY_STRING(str1); @@ -369,7 +364,7 @@ namespace UnitTest AZ_TEST_VALIDATE_STRING(str2, 28); AZ_TEST_ASSERT(str2[0] == 'b'); - str2.erase(str2.begin(), next(str2.begin(), 4)); + str2.erase(str2.begin(), AZStd::next(str2.begin(), 4)); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'f'); @@ -400,33 +395,33 @@ namespace UnitTest AZ_TEST_ASSERT(str2[3] == 'g'); AZ_TEST_ASSERT(str2[4] == 'g'); - str2.replace(str2.begin(), next(str2.begin(), str1.length()), str1); + str2.replace(str2.begin(), AZStd::next(str2.begin(), str1.length()), str1); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'a'); AZ_TEST_ASSERT(str2[1] == 'b'); - str2.replace(str2.begin(), next(str2.begin(), 10), sChar); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 10), sChar); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'S'); AZ_TEST_ASSERT(str2[1] == 'S'); - str2.replace(str2.begin(), next(str2.begin(), 3), sChar, 3); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 3), sChar, 3); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'S'); AZ_TEST_ASSERT(str2[1] == 'S'); AZ_TEST_ASSERT(str2[2] == 'O'); - str2.replace(str2.begin(), next(str2.begin(), 2), 2, 'h'); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 2), 2, 'h'); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'h'); AZ_TEST_ASSERT(str2[1] == 'h'); - str2.replace(str2.begin(), next(str2.begin(), 2), aChar.begin(), next(aChar.begin(), 2)); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 2), aChar.begin(), AZStd::next(aChar.begin(), 2)); AZ_TEST_VALIDATE_STRING(str2, 24); AZ_TEST_ASSERT(str2[0] == 'a'); AZ_TEST_ASSERT(str2[1] == 'b'); - str2.replace(str2.begin(), next(str2.begin(), 2), sChar, sChar + 5); + str2.replace(str2.begin(), AZStd::next(str2.begin(), 2), sChar, sChar + 5); AZ_TEST_VALIDATE_STRING(str2, 27); AZ_TEST_ASSERT(str2[0] == 'S'); AZ_TEST_ASSERT(str2[1] == 'S'); @@ -489,7 +484,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 2); pos = str1.find('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.rfind(str2); AZ_TEST_ASSERT(pos == 12); @@ -510,7 +505,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 12); pos = str1.rfind('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_first_of(str2); AZ_TEST_ASSERT(pos == 2); @@ -535,7 +530,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 12); pos = str1.find_first_of('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_last_of(str2); AZ_TEST_ASSERT(pos == 14); @@ -550,7 +545,7 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 12); pos = str1.find_last_of('Z'); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_first_not_of(str2, 3); AZ_TEST_ASSERT(pos == 5); @@ -559,13 +554,13 @@ namespace UnitTest AZ_TEST_ASSERT(pos == 0); pos = str1.find_last_not_of(sChar); - AZ_TEST_ASSERT(pos == string::npos); + AZ_TEST_ASSERT(pos == AZStd::string::npos); pos = str1.find_last_not_of('Z'); AZ_TEST_ASSERT(pos == 19); - string sub = str1.substr(0, 10); + AZStd::string sub = str1.substr(0, 10); AZ_TEST_VALIDATE_STRING(sub, 10); AZ_TEST_ASSERT(sub[0] == 'S'); AZ_TEST_ASSERT(sub[9] == 'g'); @@ -594,13 +589,13 @@ namespace UnitTest using iteratorType = char; auto testValue = str4; - reverse_iterator rend = testValue.rend(); - reverse_iterator crend1 = testValue.rend(); - reverse_iterator crend2 = testValue.crend(); + AZStd::reverse_iterator rend = testValue.rend(); + AZStd::reverse_iterator crend1 = testValue.rend(); + AZStd::reverse_iterator crend2 = testValue.crend(); - reverse_iterator rbegin = testValue.rbegin(); - reverse_iterator crbegin1 = testValue.rbegin(); - reverse_iterator crbegin2 = testValue.crbegin(); + AZStd::reverse_iterator rbegin = testValue.rbegin(); + AZStd::reverse_iterator crbegin1 = testValue.rbegin(); + AZStd::reverse_iterator crbegin2 = testValue.crbegin(); AZ_TEST_ASSERT(rend == crend1); AZ_TEST_ASSERT(crend1 == crend2); @@ -630,128 +625,128 @@ namespace UnitTest TEST_F(String, Algorithms) { - string str = string::format("%s %d", "BlaBla", 5); + AZStd::string str = AZStd::string::format("%s %d", "BlaBla", 5); AZ_TEST_VALIDATE_STRING(str, 8); - wstring wstr = wstring::format(L"%ls %d", L"BlaBla", 5); + AZStd::wstring wstr = AZStd::wstring::format(L"%ls %d", L"BlaBla", 5); AZ_TEST_VALIDATE_WSTRING(wstr, 8); - to_lower(str.begin(), str.end()); + AZStd::to_lower(str.begin(), str.end()); AZ_TEST_ASSERT(str[0] == 'b'); AZ_TEST_ASSERT(str[3] == 'b'); - to_upper(str.begin(), str.end()); + AZStd::to_upper(str.begin(), str.end()); AZ_TEST_ASSERT(str[1] == 'L'); AZ_TEST_ASSERT(str[2] == 'A'); - string intStr("10"); + AZStd::string intStr("10"); int ival = AZStd::stoi(intStr); AZ_TEST_ASSERT(ival == 10); - wstring wintStr(L"10"); + AZStd::wstring wintStr(L"10"); ival = AZStd::stoi(wintStr); AZ_TEST_ASSERT(ival == 10); - string floatStr("2.32"); + AZStd::string floatStr("2.32"); float fval = AZStd::stof(floatStr); AZ_TEST_ASSERT_FLOAT_CLOSE(fval, 2.32f); - wstring wfloatStr(L"2.32"); + AZStd::wstring wfloatStr(L"2.32"); fval = AZStd::stof(wfloatStr); AZ_TEST_ASSERT_FLOAT_CLOSE(fval, 2.32f); - to_string(intStr, 20); + AZStd::to_string(intStr, 20); AZ_TEST_ASSERT(intStr == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); - AZ_TEST_ASSERT(to_string(static_cast(20)) == "20"); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); + EXPECT_EQ("20", AZStd::to_string(static_cast(20))); // wstring to string - string str1; - to_string(str1, wstr); + AZStd::string str1; + AZStd::to_string(str1, wstr); AZ_TEST_ASSERT(str1 == "BlaBla 5"); EXPECT_EQ(8, to_string_length(wstr)); - str1 = string::format("%ls", wstr.c_str()); + str1 = AZStd::string::format("%ls", wstr.c_str()); AZ_TEST_ASSERT(str1 == "BlaBla 5"); // string to wstring - wstring wstr1; - to_wstring(wstr1, str); + AZStd::wstring wstr1; + AZStd::to_wstring(wstr1, str); AZ_TEST_ASSERT(wstr1 == L"BLABLA 5"); - wstr1 = wstring::format(L"%hs", str.c_str()); + wstr1 = AZStd::wstring::format(L"%hs", str.c_str()); AZ_TEST_ASSERT(wstr1 == L"BLABLA 5"); // wstring to char buffer char strBuffer[9]; - to_string(strBuffer, 9, wstr1.c_str()); + AZStd::to_string(strBuffer, 9, wstr1.c_str()); AZ_TEST_ASSERT(0 == azstricmp(strBuffer, "BLABLA 5")); EXPECT_EQ(8, to_string_length(wstr1)); // wstring to char with unicode - wstring ws1InfinityEscaped = L"Infinity: \u221E"; // escaped + AZStd::wstring ws1InfinityEscaped = L"Infinity: \u221E"; // escaped EXPECT_EQ(13, to_string_length(ws1InfinityEscaped)); // wchar_t buffer to char buffer wchar_t wstrBuffer[9] = L"BLABLA 5"; memset(strBuffer, 0, AZ_ARRAY_SIZE(strBuffer)); - to_string(strBuffer, 9, wstrBuffer); + AZStd::to_string(strBuffer, 9, wstrBuffer); AZ_TEST_ASSERT(0 == azstricmp(strBuffer, "BLABLA 5")); // string to wchar_t buffer memset(wstrBuffer, 0, AZ_ARRAY_SIZE(wstrBuffer)); - to_wstring(wstrBuffer, 9, str1.c_str()); + AZStd::to_wstring(wstrBuffer, 9, str1.c_str()); AZ_TEST_ASSERT(0 == azwcsicmp(wstrBuffer, L"BlaBla 5")); // char buffer to wchar_t buffer memset(wstrBuffer, L' ', AZ_ARRAY_SIZE(wstrBuffer)); // to check that the null terminator is properly placed - to_wstring(wstrBuffer, 9, strBuffer); + AZStd::to_wstring(wstrBuffer, 9, strBuffer); AZ_TEST_ASSERT(0 == azwcsicmp(wstrBuffer, L"BLABLA 5")); // wchar UTF16/UTF32 to/from Utf8 wstr1 = L"this is a \u20AC \u00A3 test"; // that's a euro and a pound sterling AZStd::to_string(str, wstr1); - wstring wstr2; + AZStd::wstring wstr2; AZStd::to_wstring(wstr2, str); AZ_TEST_ASSERT(wstr1 == wstr2); // tokenize - vector tokens; - tokenize(string("one, two, three"), string(", "), tokens); + AZStd::vector tokens; + AZStd::tokenize(AZStd::string("one, two, three"), AZStd::string(", "), tokens); AZ_TEST_ASSERT(tokens.size() == 3); AZ_TEST_ASSERT(tokens[0] == "one"); AZ_TEST_ASSERT(tokens[1] == "two"); AZ_TEST_ASSERT(tokens[2] == "three"); - tokenize(string("one, ,, two, ,, three"), string(", "), tokens); + AZStd::tokenize(AZStd::string("one, ,, two, ,, three"), AZStd::string(", "), tokens); AZ_TEST_ASSERT(tokens.size() == 3); AZ_TEST_ASSERT(tokens[0] == "one"); AZ_TEST_ASSERT(tokens[1] == "two"); AZ_TEST_ASSERT(tokens[2] == "three"); - tokenize(string("thequickbrownfox"), string("ABC"), tokens); + AZStd::tokenize(AZStd::string("thequickbrownfox"), AZStd::string("ABC"), tokens); AZ_TEST_ASSERT(tokens.size() == 1); AZ_TEST_ASSERT(tokens[0] == "thequickbrownfox"); - tokenize(string(""), string(""), tokens); + AZStd::tokenize(AZStd::string{}, AZStd::string{}, tokens); AZ_TEST_ASSERT(tokens.empty()); - tokenize(string("ABC"), string("ABC"), tokens); + AZStd::tokenize(AZStd::string("ABC"), AZStd::string("ABC"), tokens); AZ_TEST_ASSERT(tokens.empty()); - tokenize(string(" foo bar "), string(" "), tokens); + AZStd::tokenize(AZStd::string(" foo bar "), AZStd::string(" "), tokens); AZ_TEST_ASSERT(tokens.size() == 2); AZ_TEST_ASSERT(tokens[0] == "foo"); AZ_TEST_ASSERT(tokens[1] == "bar"); - tokenize_keep_empty(string(" foo , bar "), string(","), tokens); + AZStd::tokenize_keep_empty(AZStd::string(" foo , bar "), AZStd::string(","), tokens); AZ_TEST_ASSERT(tokens.size() == 2); AZ_TEST_ASSERT(tokens[0] == " foo "); AZ_TEST_ASSERT(tokens[1] == " bar "); // Sort - AZStd::vector toSort; + AZStd::vector toSort; toSort.push_back("z2"); toSort.push_back("z100"); toSort.push_back("z1"); @@ -761,39 +756,39 @@ namespace UnitTest AZ_TEST_ASSERT(toSort[2] == "z2"); // Natural sort - AZ_TEST_ASSERT(alphanum_comp("", "") == 0); - AZ_TEST_ASSERT(alphanum_comp("", "a") < 0); - AZ_TEST_ASSERT(alphanum_comp("a", "") > 0); - AZ_TEST_ASSERT(alphanum_comp("a", "a") == 0); - AZ_TEST_ASSERT(alphanum_comp("", "9") < 0); - AZ_TEST_ASSERT(alphanum_comp("9", "") > 0); - AZ_TEST_ASSERT(alphanum_comp("1", "1") == 0); - AZ_TEST_ASSERT(alphanum_comp("1", "2") < 0); - AZ_TEST_ASSERT(alphanum_comp("3", "2") > 0); - AZ_TEST_ASSERT(alphanum_comp("a1", "a1") == 0); - AZ_TEST_ASSERT(alphanum_comp("a1", "a2") < 0); - AZ_TEST_ASSERT(alphanum_comp("a2", "a1") > 0); - AZ_TEST_ASSERT(alphanum_comp("a1a2", "a1a3") < 0); - AZ_TEST_ASSERT(alphanum_comp("a1a2", "a1a0") > 0); - AZ_TEST_ASSERT(alphanum_comp("134", "122") > 0); - AZ_TEST_ASSERT(alphanum_comp("12a3", "12a3") == 0); - AZ_TEST_ASSERT(alphanum_comp("12a1", "12a0") > 0); - AZ_TEST_ASSERT(alphanum_comp("12a1", "12a2") < 0); - AZ_TEST_ASSERT(alphanum_comp("a", "aa") < 0); - AZ_TEST_ASSERT(alphanum_comp("aaa", "aa") > 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2", "Alpha 2") == 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2", "Alpha 2A") < 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2 B", "Alpha 2") > 0); - string strA("Alpha 2"); - AZ_TEST_ASSERT(alphanum_comp(strA, "Alpha 2") == 0); - AZ_TEST_ASSERT(alphanum_comp(strA, "Alpha 2A") < 0); - AZ_TEST_ASSERT(alphanum_comp("Alpha 2 B", strA) > 0); - AZ_TEST_ASSERT(alphanum_comp(strA, strdup("Alpha 2")) == 0); - AZ_TEST_ASSERT(alphanum_comp(strA, strdup("Alpha 2A")) < 0); - AZ_TEST_ASSERT(alphanum_comp(strdup("Alpha 2 B"), strA) > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("", "") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("", "a") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a", "") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a", "a") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("", "9") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("9", "") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("1", "1") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("1", "2") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("3", "2") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1", "a1") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1", "a2") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a2", "a1") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1a2", "a1a3") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a1a2", "a1a0") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("134", "122") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("12a3", "12a3") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("12a1", "12a0") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("12a1", "12a2") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("a", "aa") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("aaa", "aa") > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2", "Alpha 2") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2", "Alpha 2A") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2 B", "Alpha 2") > 0); + AZStd::string strA("Alpha 2"); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, "Alpha 2") == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, "Alpha 2A") < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp("Alpha 2 B", strA) > 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, strdup("Alpha 2")) == 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strA, strdup("Alpha 2A")) < 0); + AZ_TEST_ASSERT(AZStd::alphanum_comp(strdup("Alpha 2 B"), strA) > 0); // show usage of the comparison functor with a set - using StringSetType = set>; + using StringSetType = AZStd::set>; StringSetType s; s.insert("Xiph Xlater 58"); s.insert("Xiph Xlater 5000"); @@ -879,7 +874,7 @@ namespace UnitTest AZ_TEST_ASSERT(*setIt++ == "Xiph Xlater 10000"); // show usage of comparison functor with a map - using StringIntMapType = map>; + using StringIntMapType = AZStd::map>; StringIntMapType m; m["z1.doc"] = 1; m["z10.doc"] = 2; @@ -931,13 +926,13 @@ namespace UnitTest AZ_TEST_ASSERT((mapIt++)->second == 5); // show usage of comparison functor with an STL algorithm on a vector - vector v; + AZStd::vector v; // vector contents are reversed sorted contents of the old set - AZStd::copy(s.rbegin(), s.rend(), back_inserter(v)); + AZStd::copy(s.rbegin(), s.rend(), AZStd::back_inserter(v)); // now sort the vector with the algorithm - AZStd::sort(v.begin(), v.end(), alphanum_less()); + AZStd::sort(v.begin(), v.end(), AZStd::alphanum_less()); // check values - vector::const_iterator vecIt = v.begin(); + AZStd::vector::const_iterator vecIt = v.begin(); AZ_TEST_ASSERT(*vecIt++ == "10X Radonius"); AZ_TEST_ASSERT(*vecIt++ == "20X Radonius"); AZ_TEST_ASSERT(*vecIt++ == "20X Radonius Prime"); @@ -988,52 +983,52 @@ namespace UnitTest TEST_F(Regex, Regex_IPAddressSubnetPattern_Success) { // Error case for LY-43888 - regex txt_regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))?$"); - string sample_input("10.85.22.92/24"); - bool match = regex_match(sample_input, txt_regex); + AZStd::regex txt_regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))?$"); + AZStd::string sample_input("10.85.22.92/24"); + bool match = AZStd::regex_match(sample_input, txt_regex); AZ_TEST_ASSERT(match); } TEST_F(Regex, MatchConstChar) { //regex - AZ_TEST_ASSERT(regex_match("subject", regex("(sub)(.*)"))); + AZ_TEST_ASSERT(AZStd::regex_match("subject", AZStd::regex("(sub)(.*)"))); } TEST_F(Regex, MatchString) { - string reStr("subject"); - regex re("(sub)(.*)"); - AZ_TEST_ASSERT(regex_match(reStr, re)); - AZ_TEST_ASSERT(regex_match(reStr.begin(), reStr.end(), re)) + AZStd::string reStr("subject"); + AZStd::regex re("(sub)(.*)"); + AZ_TEST_ASSERT(AZStd::regex_match(reStr, re)); + AZ_TEST_ASSERT(AZStd::regex_match(reStr.begin(), reStr.end(), re)) } TEST_F(Regex, CMatch) { - regex re("(sub)(.*)"); - cmatch cm; // same as match_results cm; - regex_match("subject", cm, re); + AZStd::regex re("(sub)(.*)"); + AZStd::cmatch cm; // same as match_results cm; + AZStd::regex_match("subject", cm, re); AZ_TEST_ASSERT(cm.size() == 3); } TEST_F(Regex, SMatch) { - string reStr("subject"); - regex re("(sub)(.*)"); - smatch sm; // same as std::match_results sm; - regex_match(reStr, sm, re); + AZStd::string reStr("subject"); + AZStd::regex re("(sub)(.*)"); + AZStd::smatch sm; // same as std::match_results sm; + AZStd::regex_match(reStr, sm, re); AZ_TEST_ASSERT(sm.size() == 3); - regex_match(reStr.cbegin(), reStr.cend(), sm, re); + AZStd::regex_match(reStr.cbegin(), reStr.cend(), sm, re); AZ_TEST_ASSERT(sm.size() == 3); } TEST_F(Regex, CMatchWithFlags) { - regex re("(sub)(.*)"); - cmatch cm; // same as match_results cm; + AZStd::regex re("(sub)(.*)"); + AZStd::cmatch cm; // same as match_results cm; // using explicit flags: - regex_match("subject", cm, re, regex_constants::match_default); + AZStd::regex_match("subject", cm, re, AZStd::regex_constants::match_default); AZ_TEST_ASSERT(cm[0] == "subject"); AZ_TEST_ASSERT(cm[1] == "sub"); AZ_TEST_ASSERT(cm[2] == "ject"); @@ -1042,18 +1037,18 @@ namespace UnitTest TEST_F(Regex, PatternMatchFiles) { // Simple regular expression matching - string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; - regex txt_regex("[a-z]+\\.txt"); + AZStd::string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; + AZStd::regex txt_regex("[a-z]+\\.txt"); for (size_t i = 0; i < AZ_ARRAY_SIZE(fnames); ++i) { if (i < 2) { - AZ_TEST_ASSERT(regex_match(fnames[i], txt_regex) == true); + AZ_TEST_ASSERT(AZStd::regex_match(fnames[i], txt_regex) == true); } else { - AZ_TEST_ASSERT(regex_match(fnames[i], txt_regex) == false); + AZ_TEST_ASSERT(AZStd::regex_match(fnames[i], txt_regex) == false); } } } @@ -1061,13 +1056,13 @@ namespace UnitTest TEST_F(Regex, PatternWithSingleCaptureGroup) { // Extraction of a sub-match - string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; - regex base_regex("([a-z]+)\\.txt"); - smatch base_match; + AZStd::string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; + AZStd::regex base_regex("([a-z]+)\\.txt"); + AZStd::smatch base_match; for (size_t i = 0; i < AZ_ARRAY_SIZE(fnames); ++i) { - if (regex_match(fnames[i], base_match, base_regex)) + if (AZStd::regex_match(fnames[i], base_match, base_regex)) { AZ_TEST_ASSERT(base_match.size() == 2); AZ_TEST_ASSERT(base_match[1] == "foo" || base_match[1] == "bar") @@ -1078,12 +1073,12 @@ namespace UnitTest TEST_F(Regex, PatternWithMultipleCaptureGroups) { // Extraction of several sub-matches - string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; - regex pieces_regex("([a-z]+)\\.([a-z]+)"); - smatch pieces_match; + AZStd::string fnames[] = { "foo.txt", "bar.txt", "baz.dat", "zoidberg" }; + AZStd::regex pieces_regex("([a-z]+)\\.([a-z]+)"); + AZStd::smatch pieces_match; for (size_t i = 0; i < AZ_ARRAY_SIZE(fnames); ++i) { - if (regex_match(fnames[i], pieces_match, pieces_regex)) + if (AZStd::regex_match(fnames[i], pieces_match, pieces_regex)) { AZ_TEST_ASSERT(pieces_match.size() == 3); AZ_TEST_ASSERT(pieces_match[0] == "foo.txt" || pieces_match[0] == "bar.txt" || pieces_match[0] == "baz.dat"); @@ -1096,40 +1091,40 @@ namespace UnitTest TEST_F(Regex, WideCharTests) { //wchar_t - AZ_TEST_ASSERT(regex_match(L"subject", wregex(L"(sub)(.*)"))); - wstring reWStr(L"subject"); - wregex reW(L"(sub)(.*)"); - AZ_TEST_ASSERT(regex_match(reWStr, reW)); - AZ_TEST_ASSERT(regex_match(reWStr.begin(), reWStr.end(), reW)) + AZ_TEST_ASSERT(AZStd::regex_match(L"subject", AZStd::wregex(L"(sub)(.*)"))); + AZStd::wstring reWStr(L"subject"); + AZStd::wregex reW(L"(sub)(.*)"); + AZ_TEST_ASSERT(AZStd::regex_match(reWStr, reW)); + AZ_TEST_ASSERT(AZStd::regex_match(reWStr.begin(), reWStr.end(), reW)) } TEST_F(Regex, LongPatterns) { // test construction and destruction of a regex with a pattern long enough to require reallocation of buffers - regex longerThan16(".*\\/Presets\\/GeomCache\\/.*", regex::flag_type::icase | regex::flag_type::ECMAScript); - regex longerThan32(".*\\/Presets\\/GeomCache\\/Whatever\\/Much\\/Test\\/Very\\/Memory\\/.*", regex::flag_type::icase); + AZStd::regex longerThan16(".*\\/Presets\\/GeomCache\\/.*", AZStd::regex::flag_type::icase | AZStd::regex::flag_type::ECMAScript); + AZStd::regex longerThan32(".*\\/Presets\\/GeomCache\\/Whatever\\/Much\\/Test\\/Very\\/Memory\\/.*", AZStd::regex::flag_type::icase); } TEST_F(Regex, SmileyFaceParseRegression) { - regex smiley(":)"); + AZStd::regex smiley(":)"); EXPECT_TRUE(smiley.Empty()); EXPECT_TRUE(smiley.GetError() != nullptr); - EXPECT_FALSE(regex_match("wut", smiley)); - EXPECT_FALSE(regex_match(":)", smiley)); + EXPECT_FALSE(AZStd::regex_match("wut", smiley)); + EXPECT_FALSE(AZStd::regex_match(":)", smiley)); } TEST_F(Regex, ParseFailure) { - regex failed(")))/?!\\$"); + AZStd::regex failed(")))/?!\\$"); EXPECT_FALSE(failed.Valid()); - regex other = AZStd::move(failed); + AZStd::regex other = AZStd::move(failed); EXPECT_FALSE(other.Valid()); - regex other2; + AZStd::regex other2; other2.swap(other); EXPECT_TRUE(other.Empty()); EXPECT_TRUE(other.GetError() == nullptr); @@ -1139,69 +1134,69 @@ namespace UnitTest TEST_F(String, ConstString) { - string_view cstr1; - AZ_TEST_ASSERT(cstr1.data()==nullptr); - AZ_TEST_ASSERT(cstr1.size() == 0); - AZ_TEST_ASSERT(cstr1.length() == 0); - AZ_TEST_ASSERT(cstr1.begin() == cstr1.end()); - AZ_TEST_ASSERT(cstr1 == string_view()); - AZ_TEST_ASSERT(cstr1.empty()); - - string_view cstr2("Test"); - AZ_TEST_ASSERT(cstr2.data() != nullptr); - AZ_TEST_ASSERT(cstr2.size() == 4); - AZ_TEST_ASSERT(cstr2.length() == 4); - AZ_TEST_ASSERT(cstr2.begin() != cstr2.end()); - AZ_TEST_ASSERT(cstr2 != cstr1); - AZ_TEST_ASSERT(cstr2 == string_view("Test")); - AZ_TEST_ASSERT(cstr2 == "Test"); - AZ_TEST_ASSERT(cstr2 != "test"); - AZ_TEST_ASSERT(cstr2[2] == 's'); - AZ_TEST_ASSERT(cstr2.at(2) == 's'); + AZStd::string_view cstr1; + EXPECT_EQ(nullptr, cstr1.data()); + EXPECT_EQ(0, cstr1.size()); + EXPECT_EQ(0, cstr1.length()); + EXPECT_EQ(cstr1.begin(), cstr1.end()); + EXPECT_EQ(cstr1, AZStd::string_view()); + EXPECT_TRUE(cstr1.empty()); + + AZStd::string_view cstr2("Test"); + EXPECT_NE(nullptr, cstr2.data()); + EXPECT_EQ(4, cstr2.size()); + EXPECT_EQ(4, cstr2.length()); + EXPECT_NE(cstr2.begin(), cstr2.end()); + EXPECT_NE(cstr2, cstr1); + EXPECT_EQ(cstr2, AZStd::string_view("Test")); + EXPECT_EQ(cstr2, "Test"); + EXPECT_NE(cstr2, "test"); + EXPECT_EQ(cstr2[2], 's'); + EXPECT_EQ(cstr2.at(2), 's'); AZ_TEST_START_TRACE_SUPPRESSION; - AZ_TEST_ASSERT(cstr2.at(7) == 0); + EXPECT_EQ(0, cstr2.at(7)); AZ_TEST_STOP_TRACE_SUPPRESSION(1); - AZ_TEST_ASSERT(!cstr2.empty()); - AZ_TEST_ASSERT(cstr2.data() == string("Test")); - AZ_TEST_ASSERT((string)cstr2 == string("Test")); + EXPECT_FALSE(cstr2.empty()); + EXPECT_EQ(cstr2.data(), AZStd::string("Test")); + EXPECT_EQ(cstr2, AZStd::string("Test")); - string_view cstr3 = cstr2; - AZ_TEST_ASSERT(cstr3 == cstr2); + AZStd::string_view cstr3 = cstr2; + EXPECT_EQ(cstr3, cstr2); cstr3.swap(cstr1); - AZ_TEST_ASSERT(cstr3 == string_view()); - AZ_TEST_ASSERT(cstr1 == cstr2); + EXPECT_EQ(cstr3, AZStd::string_view()); + EXPECT_EQ(cstr1, cstr2); cstr1 = {}; - AZ_TEST_ASSERT(cstr1 == string_view()); - AZ_TEST_ASSERT(cstr1.size() == 0); - AZ_TEST_ASSERT(cstr1.length() == 0); + EXPECT_EQ(cstr1, AZStd::string_view()); + EXPECT_EQ(0, cstr1.size()); + EXPECT_EQ(0, cstr1.length()); AZStd::string str1("Test"); - AZ_TEST_ASSERT(cstr2 == str1); + EXPECT_EQ(cstr2, str1); cstr1 = str1; - AZ_TEST_ASSERT(cstr1 == cstr2); + EXPECT_EQ(cstr1, cstr2); // check hashing - AZStd::hash h; + AZStd::hash h; AZStd::size_t value = h(cstr1); - AZ_TEST_ASSERT(value != 0); + EXPECT_NE(0, value); // testing empty string AZStd::string emptyString; - string_view cstr4; + AZStd::string_view cstr4; cstr4 = emptyString; - AZ_TEST_ASSERT(cstr4.data() != nullptr); - AZ_TEST_ASSERT(cstr4.size() == 0); - AZ_TEST_ASSERT(cstr4.length() == 0); - AZ_TEST_ASSERT(cstr4.begin() == cstr4.end()); - AZ_TEST_ASSERT(cstr4.empty()); + EXPECT_NE(nullptr, cstr4.data()); + EXPECT_EQ(0, cstr4.size()); + EXPECT_EQ(0, cstr4.length()); + EXPECT_EQ(cstr4.begin(), cstr4.end()); + EXPECT_TRUE(cstr4.empty()); } TEST_F(String, StringViewModifierTest) { - string_view emptyView1; - string_view view2("Needle in Haystack"); + AZStd::string_view emptyView1; + AZStd::string_view view2("Needle in Haystack"); // front EXPECT_EQ('N', view2.front()); @@ -1209,7 +1204,7 @@ namespace UnitTest EXPECT_EQ('k', view2.back()); AZStd::string findStr("Hay"); - string_view view3(findStr); + AZStd::string_view view3(findStr); // copy const size_t destBufferSize = 32; @@ -1223,17 +1218,17 @@ namespace UnitTest AZ_TEST_STOP_TRACE_SUPPRESSION(1); // substr - string_view subView2 = view2.substr(10); + AZStd::string_view subView2 = view2.substr(10); EXPECT_EQ("Haystack", subView2); AZ_TEST_START_TRACE_SUPPRESSION; - [[maybe_unused]] string_view assertSubView = view2.substr(view2.size() + 1); + [[maybe_unused]] AZStd::string_view assertSubView = view2.substr(view2.size() + 1); AZ_TEST_STOP_TRACE_SUPPRESSION(1); // compare AZStd::size_t compareResult = view2.compare(1, view2.size() - 1, dest, copyResult); EXPECT_EQ(0, compareResult); - string_view compareView = "Stackhay in Needle"; + AZStd::string_view compareView = "Stackhay in Needle"; compareResult = compareView.compare(view2); EXPECT_NE(0, compareResult); @@ -1252,7 +1247,7 @@ namespace UnitTest EXPECT_EQ(10, findResult); findResult = compareView.find("Random String"); - EXPECT_EQ(string_view::npos, findResult); + EXPECT_EQ(AZStd::string_view::npos, findResult); findResult = view3.find('y', 2); EXPECT_EQ(2, findResult); @@ -1262,13 +1257,13 @@ namespace UnitTest EXPECT_EQ(1, rfindResult); rfindResult = emptyView1.rfind(""); - EXPECT_EQ(string_view::npos, rfindResult); + EXPECT_EQ(AZStd::string_view::npos, rfindResult); rfindResult = view2.rfind("z"); - EXPECT_EQ(string_view::npos, rfindResult); + EXPECT_EQ(AZStd::string_view::npos, rfindResult); // find_first_of - string_view repeatString = "abcdefabcfedghiabcdef"; + AZStd::string_view repeatString = "abcdefabcfedghiabcdef"; AZStd::size_t findFirstOfResult = repeatString.find_first_of('f'); EXPECT_EQ(5, findFirstOfResult); @@ -1281,7 +1276,7 @@ namespace UnitTest AZStd::string notFoundStr = "zzz"; AZStd::string foundStr = "ghi"; findFirstOfResult = repeatString.find_first_of(notFoundStr); - EXPECT_EQ(string_view::npos, findFirstOfResult); + EXPECT_EQ(AZStd::string_view::npos, findFirstOfResult); findFirstOfResult = repeatString.find_first_of(foundStr); EXPECT_EQ(12, findFirstOfResult); @@ -1297,7 +1292,7 @@ namespace UnitTest EXPECT_EQ(3, findLastOfResult); findLastOfResult = repeatString.find_last_of(notFoundStr); - EXPECT_EQ(string_view::npos, findLastOfResult); + EXPECT_EQ(AZStd::string_view::npos, findLastOfResult); findLastOfResult = repeatString.find_last_of(foundStr); EXPECT_EQ(14, findLastOfResult); @@ -1335,12 +1330,12 @@ namespace UnitTest EXPECT_EQ(11, findLastNotOfResult); // remove_prefix - string_view prefixRemovalView = view2; + AZStd::string_view prefixRemovalView = view2; prefixRemovalView.remove_prefix(6); EXPECT_EQ(" in Haystack", prefixRemovalView); // remove_suffix - string_view suffixRemovalView = view2; + AZStd::string_view suffixRemovalView = view2; suffixRemovalView.remove_suffix(8); EXPECT_EQ("Needle in ", suffixRemovalView); @@ -1365,10 +1360,10 @@ namespace UnitTest TEST_F(String, StringViewCmpOperatorTest) { - string_view view1("The quick brown fox jumped over the lazy dog"); - string_view view2("Needle in Haystack"); - string_view emptyBeaverView; - string_view superEmptyBeaverView(""); + AZStd::string_view view1("The quick brown fox jumped over the lazy dog"); + AZStd::string_view view2("Needle in Haystack"); + AZStd::string_view emptyBeaverView; + AZStd::string_view superEmptyBeaverView(""); EXPECT_EQ("", emptyBeaverView); EXPECT_EQ("", superEmptyBeaverView); @@ -1378,13 +1373,13 @@ namespace UnitTest EXPECT_EQ(view2, "Needle in Haystack"); EXPECT_NE(view2, "Needle in Hayqueue"); - string_view compareView(view2); + AZStd::string_view compareView(view2); EXPECT_EQ(view2, compareView); EXPECT_NE(view2, view1); AZStd::string compareStr("Busy Beaver"); - string_view notBeaverView("Lumber Beaver"); - string_view beaverView("Busy Beaver"); + AZStd::string_view notBeaverView("Lumber Beaver"); + AZStd::string_view beaverView("Busy Beaver"); EXPECT_EQ(compareStr, beaverView); EXPECT_NE(compareStr, notBeaverView); @@ -1528,8 +1523,8 @@ namespace UnitTest TYPED_TEST_CASE(BasicStringViewConstexprFixture, StringViewElementTypes); TYPED_TEST(BasicStringViewConstexprFixture, StringView_DefaultConstructorsIsConstexpr) { - constexpr basic_string_view defaultView1; - constexpr basic_string_view defaultView2; + constexpr AZStd::basic_string_view defaultView1; + constexpr AZStd::basic_string_view defaultView2; static_assert(defaultView1 == defaultView2, "string_view constructor should be constexpr"); } @@ -1549,7 +1544,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view charTView1(compileTimeString); + constexpr AZStd::basic_string_view charTView1(compileTimeString); static_assert(charTView1.size() == 10, "string_view constructor should be constexpr"); // non-null terminated compile time string constexpr const TypeParam* compileTimeString2 = []() constexpr -> const TypeParam* @@ -1565,7 +1560,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view charTViewWithLength(compileTimeString2, 7); + constexpr AZStd::basic_string_view charTViewWithLength(compileTimeString2, 7); static_assert(charTViewWithLength.size() == 7, "string_view constructor should be constexpr"); } @@ -1585,8 +1580,8 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view copyView1(compileTimeString); - constexpr basic_string_view copyView2(copyView1); + constexpr AZStd::basic_string_view copyView1(compileTimeString); + constexpr AZStd::basic_string_view copyView2(copyView1); static_assert(copyView1 == copyView2, "string_view constructor should be constexpr"); } @@ -1606,8 +1601,8 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view assignView1(compileTimeString1); - auto assignment_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + constexpr AZStd::basic_string_view assignView1(compileTimeString1); + auto assignment_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { constexpr const TypeParam* const compileTimeString2 = []() constexpr-> const TypeParam* { @@ -1622,7 +1617,7 @@ namespace UnitTest return {}; }(); - basic_string_view assignView2(compileTimeString2); + AZStd::basic_string_view assignView2(compileTimeString2); assignView2 = sourceView; return assignView2; }; @@ -1646,15 +1641,15 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view iteratorView(compileTimeString1); - constexpr typename basic_string_view::iterator beginIt = iteratorView.begin(); - constexpr typename basic_string_view::const_iterator cbeginIt = iteratorView.cbegin(); - constexpr typename basic_string_view::iterator endIt = iteratorView.end(); - constexpr typename basic_string_view::const_iterator cendIt = iteratorView.cend(); - constexpr typename basic_string_view::reverse_iterator rbeginIt = iteratorView.rbegin(); - constexpr typename basic_string_view::const_reverse_iterator crbeginIt = iteratorView.crbegin(); - constexpr typename basic_string_view::reverse_iterator rendIt = iteratorView.rend(); - constexpr typename basic_string_view::const_reverse_iterator crendIt = iteratorView.crend(); + constexpr AZStd::basic_string_view iteratorView(compileTimeString1); + constexpr typename AZStd::basic_string_view::iterator beginIt = iteratorView.begin(); + constexpr typename AZStd::basic_string_view::const_iterator cbeginIt = iteratorView.cbegin(); + constexpr typename AZStd::basic_string_view::iterator endIt = iteratorView.end(); + constexpr typename AZStd::basic_string_view::const_iterator cendIt = iteratorView.cend(); + constexpr typename AZStd::basic_string_view::reverse_iterator rbeginIt = iteratorView.rbegin(); + constexpr typename AZStd::basic_string_view::const_reverse_iterator crbeginIt = iteratorView.crbegin(); + constexpr typename AZStd::basic_string_view::reverse_iterator rendIt = iteratorView.rend(); + constexpr typename AZStd::basic_string_view::const_reverse_iterator crendIt = iteratorView.crend(); static_assert(beginIt != endIt, "begin and iterators should be different"); static_assert(cbeginIt != cendIt, "begin and iterators should be different"); static_assert(rbeginIt != rendIt, "begin and iterators should be different"); @@ -1679,7 +1674,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view elementView1(compileTimeString1); + constexpr AZStd::basic_string_view elementView1(compileTimeString1); static_assert(elementView1[4] == 'o', "character at index 4 in string_view should be 'o'"); static_assert(elementView1.at(5) == 'W', "character at index 5 in string_view should be 'W'"); } @@ -1700,7 +1695,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view elementView1(compileTimeString1); + constexpr AZStd::basic_string_view elementView1(compileTimeString1); static_assert(elementView1.front() == 'H', "Fourth character in string_view should be 'H'"); static_assert(elementView1.back() == 'd', "Fifth character in string_view should be 'd'"); } @@ -1734,8 +1729,8 @@ namespace UnitTest return {}; }(); - static constexpr basic_string_view elementView1(compileTimeString1); - static constexpr basic_string_view elementView2(compileTimeString2); + static constexpr AZStd::basic_string_view elementView1(compileTimeString1); + static constexpr AZStd::basic_string_view elementView2(compileTimeString2); static_assert(elementView1.data(), "string_view.data() should be non-nullptr"); static_assert(elementView2.data(), "string_view.data() should be non-nullptr"); } @@ -1756,7 +1751,7 @@ namespace UnitTest return {}; }(); - constexpr basic_string_view sizeView1(compileTimeString1); + constexpr AZStd::basic_string_view sizeView1(compileTimeString1); static_assert(sizeView1.size() == sizeView1.length(), "string_views size and length function should return the same value"); static_assert(!sizeView1.empty(), "string_views should not be empty"); static_assert(sizeView1.max_size() != 0, "string_views max_size should be greater than 0"); @@ -1770,21 +1765,21 @@ namespace UnitTest { return "HelloWorld"; }; - constexpr basic_string_view modifierView("HelloWorld"); + constexpr AZStd::basic_string_view modifierView("HelloWorld"); // A constexpr lambda is used to evaluate non constexpr string_view instances' member functions which // have been marked as constexpr at compile time // The google test function being run is not a constexpr function and therefore will evaulate // non-constexpr string_view variables at runtime. This would cause static_assert to state // that the expression is evaluated at runtime - auto remove_prefix_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + auto remove_prefix_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { - basic_string_view lstripView(sourceView); + AZStd::basic_string_view lstripView(sourceView); lstripView.remove_prefix(5); return lstripView; }; - auto remove_suffix_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + auto remove_suffix_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { - basic_string_view rstripView(sourceView); + AZStd::basic_string_view rstripView(sourceView); rstripView.remove_suffix(5); return rstripView; }; @@ -1801,8 +1796,8 @@ namespace UnitTest return "HelloWorld"; }; constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();; - constexpr basic_string_view fullView(compileTimeString1); - auto substr_test_func = [](basic_string_view sourceView) constexpr -> basic_string_view + constexpr AZStd::basic_string_view fullView(compileTimeString1); + auto substr_test_func = [](AZStd::basic_string_view sourceView) constexpr -> AZStd::basic_string_view { return sourceView.substr(3, 5); }; @@ -1818,7 +1813,7 @@ namespace UnitTest return "elloGovernor"; }; constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); - constexpr basic_string_view withView(compileTimeString1); + constexpr AZStd::basic_string_view withView(compileTimeString1); static_assert(withView.starts_with("ello"), "string_view should start with \"ello\""); // Regression in VS2017 15.8 and 15.9 where __builtin_memcmp fails in valid checks #if AZ_COMPILER_MSVC < 1915 && AZ_COMPILER_MSVC > 1916 @@ -1854,9 +1849,9 @@ namespace UnitTest TYPED_TEST(BasicStringViewConstexprFixture, StringView_FindOperationsAreConstexpr) { constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1; - constexpr basic_string_view quickFoxView(compileTimeString1); + constexpr AZStd::basic_string_view quickFoxView(compileTimeString1); constexpr const TypeParam* searchString = MakeSearchString; - constexpr basic_string_view searchView(searchString); + constexpr AZStd::basic_string_view searchView(searchString); constexpr const TypeParam* testString1 = MakeTestString1; constexpr const TypeParam* testString2 = MakeTestString2; @@ -1893,10 +1888,10 @@ namespace UnitTest static_assert(quickFoxView.find_last_of('o') == 42, "string_view find_last_of should result in index 42"); static_assert(quickFoxView.find_last_of(testString6) == 40, "string_view find_last_of should result in index 40"); static_assert(quickFoxView.find_last_of(testString7, 31) == 29, "string_view find_last_of should result in index 29"); - static_assert(quickFoxView.find_last_of(testString8, basic_string_view::npos, 1) == 7, "string_view find_last_of should result in index 7"); + static_assert(quickFoxView.find_last_of(testString8, AZStd::basic_string_view::npos, 1) == 7, "string_view find_last_of should result in index 7"); // find_first_not_of test - constexpr basic_string_view firstNotOfView(testString9); + constexpr AZStd::basic_string_view firstNotOfView(testString9); static_assert(quickFoxView.find_first_not_of(firstNotOfView) == 4, "string_view find_first_not_of should result in index 0"); static_assert(quickFoxView.find_first_not_of('t') == 1, "string_view find_first_not_of should result in index 1"); static_assert(quickFoxView.find_first_not_of(testString9) == 4, "string_view find_first_not_of should result in index 4"); @@ -1904,12 +1899,12 @@ namespace UnitTest static_assert(quickFoxView.find_first_not_of(testString9, 0, 1) == 1, "string_view find_first_not_of should result in index 1"); // find_last_not_of test - constexpr basic_string_view lastNotOfView(testString10); + constexpr AZStd::basic_string_view lastNotOfView(testString10); static_assert(quickFoxView.find_last_not_of(lastNotOfView) == 39, "string_view find_last_not_of should result in index 39"); static_assert(quickFoxView.find_last_not_of('g') == 42, "string_view find_last_not_of should result in index 42"); static_assert(quickFoxView.find_last_not_of(testString10) == 39, "string_view find_last_not_of should result in index 39"); static_assert(quickFoxView.find_last_not_of(testString10, 27) == 24, "string_view find_last_not_of should result in index 24"); - static_assert(quickFoxView.find_last_not_of(testString10, basic_string_view::npos, 1) == 43, "string_view find_last_not_of should result in index 43"); + static_assert(quickFoxView.find_last_not_of(testString10, AZStd::basic_string_view::npos, 1) == 43, "string_view find_last_not_of should result in index 43"); } TEST_F(String, StringView_CompareIsConstexpr) @@ -1925,8 +1920,8 @@ namespace UnitTest }; constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); - constexpr basic_string_view lhsView(compileTimeString1); - constexpr basic_string_view rhsView(compileTimeString2); + constexpr AZStd::basic_string_view lhsView(compileTimeString1); + constexpr AZStd::basic_string_view rhsView(compileTimeString2); static_assert(lhsView.compare(rhsView) > 0, R"("HelloWorld" > "HelloPearl")"); static_assert(lhsView.compare(0, 5, rhsView) < 0, R"("Hello" < HelloPearl")"); static_assert(lhsView.compare(2, 3, rhsView, 2, 3) == 0, R"("llo" == llo")"); @@ -1943,7 +1938,7 @@ namespace UnitTest return "HelloWorld"; }; constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1(); - constexpr basic_string_view compareView(compileTimeString1); + constexpr AZStd::basic_string_view compareView(compileTimeString1); static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed"); static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed"); static_assert(compareView < "JelloWorld", "string_view operator< comparison has failed"); @@ -1954,7 +1949,7 @@ namespace UnitTest TYPED_TEST(BasicStringViewConstexprFixture, StringView_SwapIsConstexpr) { - auto swap_test_func = []() constexpr -> basic_string_view + auto swap_test_func = []() constexpr -> AZStd::basic_string_view { constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { @@ -1980,8 +1975,8 @@ namespace UnitTest }; constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); - basic_string_view lhsView(compileTimeString1); - basic_string_view rhsView(compileTimeString2); + AZStd::basic_string_view lhsView(compileTimeString1); + AZStd::basic_string_view rhsView(compileTimeString2); lhsView.swap(rhsView); return lhsView; }; @@ -2014,13 +2009,14 @@ namespace UnitTest } }; constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); - constexpr basic_string_view hashView(compileTimeString1); - constexpr size_t compileHash = AZStd::hash>{}(hashView); + constexpr AZStd::basic_string_view hashView(compileTimeString1); + constexpr size_t compileHash = AZStd::hash>{}(hashView); static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0"); } TEST_F(String, StringView_UserLiteralsSucceed) { + using namespace AZStd::string_view_literals; constexpr auto charView{ "Test"_sv }; constexpr auto wcharView{ L"Super Test"_sv }; static_assert(charView == "Test", "char string literal should be \"Test\""); @@ -2291,21 +2287,21 @@ namespace UnitTest { AZStd::fixed_string<32> filter1; AZStd::string testValue{ "test" }; - EXPECT_FALSE(wildcard_match(filter1, testValue)); + EXPECT_FALSE(AZStd::wildcard_match(filter1, testValue)); } TEST_F(String, WildcardMatch_EmptyFilterWithEmptyValue_Succeeds) { AZStd::fixed_string<32> filter1; AZStd::fixed_string<32> emptyValue; - EXPECT_TRUE(wildcard_match(filter1, emptyValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, emptyValue)); } TEST_F(String, WildcardMatch_AsteriskOnlyFilterWithEmptyValue_Succeeds) { const char* filter1{ "*" }; const char* filter2{ "**" }; const char* emptyValue{ "" }; - EXPECT_TRUE(wildcard_match(filter1, emptyValue)); - EXPECT_TRUE(wildcard_match(filter2, emptyValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, emptyValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter2, emptyValue)); } TEST_F(String, WildcardMatch_AsteriskQuestionMarkFilterWithEmptyValue_Failes) { @@ -2313,60 +2309,60 @@ namespace UnitTest const char* filter1{ "*?" }; const char* filter2{ "?*" }; const char* emptyValue{ "" }; - EXPECT_FALSE(wildcard_match(filter1, emptyValue)); - EXPECT_FALSE(wildcard_match(filter2, emptyValue)); + EXPECT_FALSE(AZStd::wildcard_match(filter1, emptyValue)); + EXPECT_FALSE(AZStd::wildcard_match(filter2, emptyValue)); } TEST_F(String, WildcardMatch_DotValue_Succeeds) { const char* filter1{ "?" }; const char* dotValue{ "." }; - EXPECT_TRUE(wildcard_match(filter1, dotValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, dotValue)); } TEST_F(String, WildcardMatch_DoubleDotValue_Succeeds) { const char* filter1{ "??" }; const char* dotValue{ ".." }; - EXPECT_TRUE(wildcard_match(filter1, dotValue)); + EXPECT_TRUE(AZStd::wildcard_match(filter1, dotValue)); } TEST_F(String, WildcardMatch_GlobFilters_Succeeds) { const char* filter1{ "*" }; const char* filter2{ "*?" }; const char* filter3{ "?*" }; - EXPECT_TRUE(wildcard_match(filter1, "Hello")); - EXPECT_TRUE(wildcard_match(filter1, "?")); - EXPECT_TRUE(wildcard_match(filter1, "*")); - EXPECT_TRUE(wildcard_match(filter1, "Q")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "Hello")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "?")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "*")); + EXPECT_TRUE(AZStd::wildcard_match(filter1, "Q")); - EXPECT_TRUE(wildcard_match(filter2, "Hello")); - EXPECT_TRUE(wildcard_match(filter2, "?")); - EXPECT_TRUE(wildcard_match(filter2, "*")); - EXPECT_TRUE(wildcard_match(filter2, "Q")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "Hello")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "?")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "*")); + EXPECT_TRUE(AZStd::wildcard_match(filter2, "Q")); - EXPECT_TRUE(wildcard_match(filter3, "Hello")); - EXPECT_TRUE(wildcard_match(filter3, "?")); - EXPECT_TRUE(wildcard_match(filter3, "*")); - EXPECT_TRUE(wildcard_match(filter3, "Q")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "Hello")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "?")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "*")); + EXPECT_TRUE(AZStd::wildcard_match(filter3, "Q")); } TEST_F(String, WildcardMatch_NormalString_Succeeds) { constexpr AZStd::string_view jpgFilter{ "**/*.jpg" }; - EXPECT_FALSE(wildcard_match(jpgFilter, "Test.jpg")); - EXPECT_FALSE(wildcard_match(jpgFilter, "Test.jpfg")); - EXPECT_TRUE(wildcard_match(jpgFilter, "Images/Other.jpg")); - EXPECT_FALSE(wildcard_match(jpgFilter, "Pictures/Other.gif")); + EXPECT_FALSE(AZStd::wildcard_match(jpgFilter, "Test.jpg")); + EXPECT_FALSE(AZStd::wildcard_match(jpgFilter, "Test.jpfg")); + EXPECT_TRUE(AZStd::wildcard_match(jpgFilter, "Images/Other.jpg")); + EXPECT_FALSE(AZStd::wildcard_match(jpgFilter, "Pictures/Other.gif")); constexpr AZStd::string_view tempDirFilter{ "temp/*" }; - EXPECT_TRUE(wildcard_match(tempDirFilter, "temp/")); - EXPECT_TRUE(wildcard_match(tempDirFilter, "temp/f")); - EXPECT_FALSE(wildcard_match(tempDirFilter, "tem1/")); + EXPECT_TRUE(AZStd::wildcard_match(tempDirFilter, "temp/")); + EXPECT_TRUE(AZStd::wildcard_match(tempDirFilter, "temp/f")); + EXPECT_FALSE(AZStd::wildcard_match(tempDirFilter, "tem1/")); constexpr AZStd::string_view xmlFilter{ "test.xml" }; - EXPECT_TRUE(wildcard_match(xmlFilter, "Test.xml")); - EXPECT_TRUE(wildcard_match(xmlFilter, "test.xml")); - EXPECT_FALSE(wildcard_match(xmlFilter, "test.xmlschema")); - EXPECT_FALSE(wildcard_match(xmlFilter, "Xtest.xml")); + EXPECT_TRUE(AZStd::wildcard_match(xmlFilter, "Test.xml")); + EXPECT_TRUE(AZStd::wildcard_match(xmlFilter, "test.xml")); + EXPECT_FALSE(AZStd::wildcard_match(xmlFilter, "test.xmlschema")); + EXPECT_FALSE(AZStd::wildcard_match(xmlFilter, "Xtest.xml")); } TEST_F(String, WildcardMatchCase_CanBeCompileTimeEvaluated_Succeeds) @@ -2424,6 +2420,31 @@ namespace UnitTest EXPECT_EQ("oWord", eraseIfTest); } + TEST_F(String, StringWithStatelessAllocator_HasSizeOf_PointerPlus2IntTypes_Compiles) + { + // The expected size of a basic_string with a stateless allocator + // Is the size of the pointer (used for storing the memory address of the string) + // + the size of the string "size" member used to store the size of the string + // + the size of the string "capacity" member used to store the capacity of the string + size_t constexpr ExpectedBasicStringSize = sizeof(void*) + 2 * sizeof(size_t); + using StringStatelessAllocator = AZStd::basic_string, AZStd::stateless_allocator>; + static_assert(ExpectedBasicStringSize == sizeof(StringStatelessAllocator), + "Stateless allocator is counting against the size of the basic_string class" + " A change has made to break the empty base optimization of the basic_string class"); + } + + TEST_F(String, StringWithStatefulAllocator_HasSizeOf_PointerPlus2IntTypesPlusAllocator_Compiles) + { + // The expected size of a basic_string with a stateless allocator + // Is the size of the pointer (used for storing the memory address of the string) + // + the size of the string "size" member used to store the size of the string + // + the size of the string "capacity" member used to store the capacity of the string + size_t constexpr ExpectedBasicStringSize = sizeof(void*) + 2 * sizeof(size_t) + sizeof(AZStd::allocator); + static_assert(ExpectedBasicStringSize == sizeof(AZStd::string), + "Using Stateful allocator with basic_string class should result in a 32-byte string class" + " on 64-bit platforms "); + } + template class ImmutableStringFunctionsFixture : public ScopedAllocatorSetupFixture @@ -2473,5 +2494,296 @@ namespace UnitTest EXPECT_EQ(str, formatted); } -#endif // AZ_UNIT_TEST_SKIP_STD_STRING_TESTS } + +#if defined(HAVE_BENCHMARK) +namespace Benchmark +{ + class StringBenchmarkFixture + : public ::UnitTest::AllocatorsBenchmarkFixture + { + protected: + template + void SwapStringViaMemcpy(AZStd::basic_string& left, + AZStd::basic_string& right) + { + // Test Swapping the storage container for the string class + // Use aligned_storage to prevent constructors from slowing operation + AZStd::aligned_storage_for_t tempStorage; + ::memcpy(&tempStorage, &left.m_storage.first(), sizeof(left.m_storage.first())); + ::memcpy(&left.m_storage.first(), &right.m_storage.first(), sizeof(right.m_storage.first())); + ::memcpy(&right.m_storage.first(), &tempStorage, sizeof(tempStorage)); + } + + + template + void SwapStringViaPointerSizedSwaps(AZStd::basic_string& left, + AZStd::basic_string& right) + { + using String = AZStd::basic_string; + using PointerAlignedData = typename String::PointerAlignedData; + // Use pointer sized swaps to swap the string storage + auto& leftAlignedPointers = reinterpret_cast(left.m_storage.first()); + auto& rightAlignedPointers = reinterpret_cast(right.m_storage.first()); + constexpr size_t alignedPointerCount{ AZStd::size(PointerAlignedData{}.m_alignedValues) }; + for (size_t i = 0; i < alignedPointerCount; ++i) + { + AZStd::swap(leftAlignedPointers.m_alignedValues[i], rightAlignedPointers.m_alignedValues[i]); + } + } + }; + + BENCHMARK_F(StringBenchmarkFixture, BM_StringPointerSwapShortString)(benchmark::State& state) + { + AZStd::string test1{ "foo bar"}; + AZStd::string test2{ "bar foo" }; + for (auto _ : state) + { + SwapStringViaPointerSizedSwaps(test1, test2); + } + } + + BENCHMARK_F(StringBenchmarkFixture, BM_StringPointerSwapLongString)(benchmark::State& state) + { + AZStd::string test1{ "The brown quick wolf jumped over the hyperactive cat" }; + AZStd::string test2{ "The quick brown fox jumped over the lazy dog" }; + for (auto _ : state) + { + SwapStringViaPointerSizedSwaps(test1, test2); + } + } + + BENCHMARK_F(StringBenchmarkFixture, BM_StringMemcpySwapShortString)(benchmark::State& state) + { + AZStd::string test1{ "foo bar" }; + AZStd::string test2{ "bar foo" }; + for (auto _ : state) + { + SwapStringViaMemcpy(test1, test2); + } + } + + BENCHMARK_F(StringBenchmarkFixture, BM_StringMemcpySwapLongString)(benchmark::State& state) + { + AZStd::string test1{ "The brown quick wolf jumped over the hyperactive cat" }; + AZStd::string test2{ "The quick brown fox jumped over the lazy dog" }; + for (auto _ : state) + { + SwapStringViaMemcpy(test1, test2); + } + } + + template + class StringTemplateBenchmarkFixture + : public ::UnitTest::AllocatorsBenchmarkFixture + {}; + + // AZStd::string assign benchmarks + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_NullDelimited, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceAddress); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_NullDelimited) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_WithSize, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + const size_t sourceSize = sourceString.size(); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceAddress, sourceSize); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignConstPointer_WithSize) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromIterators, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + auto sourceBegin = sourceString.begin(); + auto sourceEnd = sourceString.end(); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceBegin, sourceEnd); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromIterators) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromStringView, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + AZStd::string_view sourceView(sourceString); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceView); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromStringView) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_LValue, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(sourceString); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_LValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_RValue, AZStd::string)(benchmark::State& state) + { + AZStd::string sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(AZStd::move(sourceString)); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromString_RValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromSingleCharacter, AZStd::string)(benchmark::State& state) + { + for (auto _ : state) + { + AZStd::string assignString; + assignString.assign(state.range(0), 'a'); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_StringAssignFromSingleCharacter) + ->RangeMultiplier(2)->Range(8, 32); + + // AZStd::fixed_string assign benchmarks + // NOTE: This is a copy-and-paste of above because Google Benchmark doesn't support real templated benchmarks like Googletest + // https://github.com/google/benchmark/issues/541 + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_NullDelimited, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceAddress); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_NullDelimited) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_WithSize, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + const char* sourceAddress = sourceString.c_str(); + const size_t sourceSize = sourceString.size(); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceAddress, sourceSize); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignConstPointer_WithSize) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromIterators, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + auto sourceBegin = sourceString.begin(); + auto sourceEnd = sourceString.end(); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceBegin, sourceEnd); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromIterators) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromStringView, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + AZStd::string_view sourceView(sourceString); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceView); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromStringView) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_LValue, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(sourceString); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_LValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_RValue, AZStd::fixed_string<1024>)(benchmark::State& state) + { + AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); + + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(AZStd::move(sourceString)); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromString_RValue) + ->RangeMultiplier(2)->Range(8, 32); + + BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromSingleCharacter, AZStd::fixed_string<1024>)(benchmark::State& state) + { + for (auto _ : state) + { + AZStd::fixed_string<1024> assignString; + assignString.assign(state.range(0), 'a'); + } + } + + BENCHMARK_REGISTER_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromSingleCharacter) + ->RangeMultiplier(2)->Range(8, 32); +} +#endif diff --git a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp index 7ce0290ea3..e17d5c1fbf 100644 --- a/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp +++ b/Gems/AWSCore/Code/Tests/Editor/Attribution/AWSAttributionServiceApiTest.cpp @@ -72,12 +72,11 @@ namespace AWSCoreUnitTest AWSCore::RequestBuilder requestBuilder{}; EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); std::shared_ptr bodyContent = requestBuilder.GetBodyContent(); - EXPECT_TRUE(bodyContent != nullptr); + EXPECT_NE(nullptr, bodyContent); - AZStd::string bodyString; std::istreambuf_iterator eos; - bodyString = AZStd::string{ std::istreambuf_iterator(*bodyContent), eos }; - AZ_Printf("AWSAttributionServiceApiTest", bodyString.c_str()); - EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion)) != AZStd::string::npos); + AZStd::string bodyString{ std::istreambuf_iterator(*bodyContent), eos }; + AZ_Printf("AWSAttributionServiceApiTest", "%s", bodyString.c_str()); + EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion))); } } diff --git a/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp b/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp index 8844e727b6..b7e7527b20 100644 --- a/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp +++ b/Gems/AWSMetrics/Code/Tests/AWSMetricsServiceApiTest.cpp @@ -100,11 +100,10 @@ namespace AWSMetrics AWSCore::RequestBuilder requestBuilder{}; EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); std::shared_ptr bodyContent = requestBuilder.GetBodyContent(); - EXPECT_TRUE(bodyContent != nullptr); + ASSERT_NE(nullptr, bodyContent); - AZStd::string bodyString; std::istreambuf_iterator eos; - bodyString = AZStd::string{ std::istreambuf_iterator(*bodyContent), eos }; - EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents)) != AZStd::string::npos); + AZStd::string bodyString{ std::istreambuf_iterator(*bodyContent), eos }; + EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents))); } } diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp index bd3fa10b24..11865674c8 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphEventTests.cpp @@ -49,7 +49,7 @@ namespace EMotionFX for (int i = 0; i < params.m_numStates; ++i) { AnimGraphNode* state = aznew AnimGraphMotionNode(); - state->SetName(AZStd::string(1, startChar + i).c_str()); + state->SetName(AZStd::string(1, static_cast(startChar + i)).c_str()); m_rootStateMachine->AddChildNode(state); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/params.m_transitionBlendTime, /*countDownTime*/params.m_conditionCountDownTime); prevState = state; diff --git a/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp b/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp index 54f945d95f..c4e6462fff 100644 --- a/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp +++ b/Gems/EMotionFX/Code/Tests/AnimGraphRefCountTests.cpp @@ -90,7 +90,7 @@ namespace EMotionFX for (int i = 0; i < param.m_numStates; ++i) { AnimGraphBindPoseNode* state = aznew AnimGraphBindPoseNode(); - state->SetName(AZStd::string(1, startChar + i).c_str()); + state->SetName(AZStd::string(1, static_cast(startChar + i)).c_str()); m_rootStateMachine->AddChildNode(state); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/param.m_blendTime, /*countDownTime*/param.m_countDownTime); prevState = state;