AZStd::basic_string improvements (#6438)

* AZStd::basic_string improvements

The AZStd::basic_string class has a better implementation of the Short
String Optimization, which increases the amount of characters that can
be stored in a `basic_string<char>` from 15 characters to 22
characters(not-including null-terminating characters). For a
`basic_string<wchar_t>` on Windows the amount of characters that can be
stored increases from 7 to 10. Using `basic_string<wchar_t>` on Unix
platforms SSO character amount from 3 to 4 characters.

An additional benefit is that the size of the AZStd::basic_string class
has been reduced from 40 bytes to 32 bytes when using the
AZStd::allocator.
When using a stateless allocator with no non static data members such as
AZStd::stateless_allocator, the size of the AZStd::basic_string is 24
bytes.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Corrected comments and updated type alias to usings for AZStd::basic_string

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Added Benchmarks for the basic_string and basic_fixed_string class

The benchmarks currently measure the speed of the `assign` overloads.
A benchmark has also been added to compare the speed swapping two
`basic_string` instances by 3 memcpy vs 3 pointer swap operations

Speed up string operation when in the iterator overload cases of the
`assign`, `append`, `insert` and `replace` function.
The code was always performing the logic to copy over a string that is
overlapping, without actually checking if the string was overlapping in
the first place.

Added an `az_builtin_is_constant_evaluated` macro that allows use of the
C++20 `std::is_constant_evaluated` feature to determine if an operation
is being performed at compile time vs run time.

That macro is being used to speed up the char_trait operations at run
time, by using the faster standard library functions.
For example char_traits::move now uses "memmove" at runtime, instead of
a for loop.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Simplified string logic in AWSMetricsServiceApiTest.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
This commit is contained in:
lumberyard-employee-dm
2022-01-10 10:03:31 -06:00
committed by GitHub
parent 7c88f20e1e
commit 098005afbc
15 changed files with 1860 additions and 1317 deletions
@@ -149,3 +149,79 @@
#if !defined(AZ_COMMAND_LINE_LEN) #if !defined(AZ_COMMAND_LINE_LEN)
# define AZ_COMMAND_LINE_LEN 2048 # define AZ_COMMAND_LINE_LEN 2048
#endif #endif
#include <type_traits>
#include <utility>
#include <memory>
#include <cstdint>
#include <cstring>
// 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
@@ -11,17 +11,17 @@
namespace AZStd namespace AZStd
{ {
stateless_allocator::stateless_allocator(const char* name) stateless_allocator::stateless_allocator() = default;
: m_name(name) {} stateless_allocator::stateless_allocator(const char*)
{}
const char* stateless_allocator::get_name() const 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 auto stateless_allocator::allocate(size_type byteSize) -> pointer_type
@@ -26,7 +26,8 @@ namespace AZStd
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using allow_memory_leaks = AZStd::true_type; 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(const stateless_allocator& rhs) = default;
stateless_allocator& operator=(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_lock_free();
bool is_stale_read_allowed(); bool is_stale_read_allowed();
bool is_delayed_recycling(); bool is_delayed_recycling();
private:
const char* m_name;
}; };
bool operator==(const stateless_allocator& left, const stateless_allocator& right); bool operator==(const stateless_allocator& left, const stateless_allocator& right);
@@ -8,6 +8,7 @@
#pragma once #pragma once
#include <AzCore/std/utils.h> #include <AzCore/std/utils.h>
#include <AzCore/std/typetraits/is_constructible.h>
#include <AzCore/std/typetraits/remove_cvref.h> #include <AzCore/std/typetraits/remove_cvref.h>
/* Microsoft C++ ABI puts 1 byte of padding between each empty base class when multiple inheritance is being used /* 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) #if defined(AZ_COMPILER_MSVC)
#define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION __declspec(empty_bases) #define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION __declspec(empty_bases)
#else #else
#define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION #define AZSTD_COMPRESSED_PAIR_EMPTY_BASE_OPTIMIZATION
#endif #endif
namespace AZStd namespace AZStd
@@ -97,16 +98,14 @@ namespace AZStd
using second_base_value_type = typename second_base_type::value_type; using second_base_value_type = typename second_base_type::value_type;
public: public:
// First template argument is a placeholder argument of void as MSVC examines the types // First template argument is used to perform a substitution into AZStd::enable_if_t
// of a templated function to determine if they are the same template // so that SFINAE can trigger
// Due to the "template <class T> compressed_pair(skip_element_tag, T&&)" template <typename Unused = void, typename = AZStd::enable_if_t<
// constructor below, the default constructor template types needs to be distinguished from it AZStd::is_default_constructible_v<first_base_value_type>
template <typename = void, typename = AZStd::enable_if_t< && AZStd::is_default_constructible_v<second_base_value_type>, Unused>>
AZStd::is_default_constructible<first_base_value_type>::value
&& AZStd::is_default_constructible<second_base_value_type>::value>>
constexpr compressed_pair(); constexpr compressed_pair();
template <typename T, AZStd::enable_if_t<!is_same<remove_cvref_t<T>, compressed_pair>::value, bool> = true> template <typename T, AZStd::enable_if_t<!is_same_v<remove_cvref_t<T>, compressed_pair>, bool> = true>
constexpr explicit compressed_pair(T&& firstElement); constexpr explicit compressed_pair(T&& firstElement);
template <typename T> template <typename T>
@@ -75,7 +75,7 @@ namespace AZStd
} }
template <typename T1, typename T2> template <typename T1, typename T2>
template <typename T, AZStd::enable_if_t<!is_same<remove_cvref_t<T>, compressed_pair<T1, T2>>::value, bool>> template <typename T, AZStd::enable_if_t<!is_same_v<remove_cvref_t<T>, compressed_pair<T1, T2>>, bool>>
inline constexpr compressed_pair<T1, T2>::compressed_pair(T&& firstElement) inline constexpr compressed_pair<T1, T2>::compressed_pair(T&& firstElement)
: first_base_type{ AZStd::forward<T>(firstElement) } : first_base_type{ AZStd::forward<T>(firstElement) }
, second_base_type{} , second_base_type{}
@@ -117,7 +117,7 @@ namespace AZStd
{ {
return static_cast<const first_base_type&>(*this).get(); return static_cast<const first_base_type&>(*this).get();
} }
template <typename T1, typename T2> template <typename T1, typename T2>
inline constexpr auto compressed_pair<T1, T2>::second() -> second_base_value_type& inline constexpr auto compressed_pair<T1, T2>::second() -> second_base_value_type&
{ {
@@ -343,26 +343,6 @@ namespace AZStd
static decltype(auto) format(const wchar_t* format, ...); static decltype(auto) format(const wchar_t* format, ...);
protected: protected:
template<class InputIt>
constexpr auto append_iter(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
template<class InputIt>
constexpr auto construct_iter(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>>;
template<class InputIt>
constexpr auto assign_iter(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
template<class InputIt>
constexpr auto insert_iter(const_iterator insertPos, InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>;
template<class InputIt>
constexpr auto replace_iter(const_iterator first, const_iterator last, InputIt first2, InputIt last2)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
constexpr auto fits_in_capacity(size_type newSize) -> bool; 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 inline static constexpr size_type Capacity = MaxElementCount; // current storage reserved for string not including null-terminator
@@ -62,14 +62,7 @@ namespace AZStd
template<class InputIt, typename> template<class InputIt, typename>
inline constexpr basic_fixed_string<Element, MaxElementCount, Traits>::basic_fixed_string(InputIt first, InputIt last) inline constexpr basic_fixed_string<Element, MaxElementCount, Traits>::basic_fixed_string(InputIt first, InputIt last)
{ // construct from [first, last) { // construct from [first, last)
if (first == last) assign(first, last);
{
Traits::assign(m_buffer[0], Element()); // terminate
}
else
{
construct_iter(first, last);
}
} }
// #7 // #7
@@ -98,8 +91,7 @@ namespace AZStd
template<typename T, typename> template<typename T, typename>
inline constexpr basic_fixed_string<Element, MaxElementCount, Traits>::basic_fixed_string(const T& convertibleToView) inline constexpr basic_fixed_string<Element, MaxElementCount, Traits>::basic_fixed_string(const T& convertibleToView)
{ {
AZStd::basic_string_view<Element, Traits> view = convertibleToView; assign(convertibleToView);
assign(view.begin(), view.end());
} }
// #11 // #11
@@ -313,15 +305,7 @@ namespace AZStd
if (count > 0 && fits_in_capacity(num)) if (count > 0 && fits_in_capacity(num))
{ {
pointer data = m_buffer; pointer data = m_buffer;
// make room and append new stuff using assign Traits::assign(data + m_size, count, ch);
if (count == 1)
{
Traits::assign(*(data + m_size), ch);
}
else
{
Traits::assign(data + m_size, count, ch);
}
m_size = static_cast<internal_size_type>(num); m_size = static_cast<internal_size_type>(num);
Traits::assign(data[num], Element()); // terminate Traits::assign(data[num], Element()); // terminate
} }
@@ -332,13 +316,47 @@ namespace AZStd
template<class InputIt> template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::append(InputIt first, InputIt last) inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::append(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&> -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{ // append [first, last) {
return append_iter(first, last); if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
&& is_same_v<typename AZStd::iterator_traits<InputIt>::value_type, value_type>)
{
return append(AZStd::to_address(first), AZStd::distance(first, last));
}
else if constexpr (Internal::is_forward_iterator_v<InputIt>)
{
// 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<Element>(*first));
}
m_size = static_cast<internal_size_type>(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<Element>(*first));
}
return append(inputCopy.c_str(), inputCopy.size());
}
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::append(AZStd::initializer_list<Element> ilist) -> basic_fixed_string& inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::append(AZStd::initializer_list<Element> ilist) -> basic_fixed_string&
{ // append [first, last) {
return append_iter(ilist.begin(), ilist.end()); return append(ilist.begin(), ilist.size());
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
@@ -420,18 +438,10 @@ namespace AZStd
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(size_type count, Element ch) -> basic_fixed_string& inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(size_type count, Element ch) -> basic_fixed_string&
{ {
// assign count * ch // assign count * ch
AZSTD_CONTAINER_ASSERT(count != npos, "result is too long!");
if (fits_in_capacity(count)) if (fits_in_capacity(count))
{ // make room and assign new stuff { // make room and assign new stuff
pointer data = m_buffer; pointer data = m_buffer;
if (count == 1) Traits::assign(data, count, ch);
{
Traits::assign(*(data), ch);
}
else
{
Traits::assign(data, count, ch);
}
m_size = static_cast<internal_size_type>(count); m_size = static_cast<internal_size_type>(count);
Traits::assign(data[count], Element()); // terminate Traits::assign(data[count], Element()); // terminate
} }
@@ -443,12 +453,46 @@ namespace AZStd
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(InputIt first, InputIt last) inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&> -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{ {
return assign_iter(first, last); if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
&& is_same_v<typename AZStd::iterator_traits<InputIt>::value_type, value_type>)
{
return assign(AZStd::to_address(first), AZStd::distance(first, last));
}
else if constexpr (Internal::is_forward_iterator_v<InputIt>)
{
// 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<Element>(*first));
}
m_size = static_cast<internal_size_type>(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<Element>(*first));
}
return assign(inputCopy.c_str(), inputCopy.size());
}
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(AZStd::initializer_list<Element> ilist) -> basic_fixed_string& inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(AZStd::initializer_list<Element> ilist) -> basic_fixed_string&
{ {
return assign_iter(ilist.begin(), ilist.end()); return assign(ilist.begin(), ilist.size());
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
@@ -536,14 +580,7 @@ namespace AZStd
pointer data = m_buffer; pointer data = m_buffer;
// make room and insert new stuff // make room and insert new stuff
Traits::copy_backward(data + offset + count, data + offset, m_size - offset); // empty out hole Traits::copy_backward(data + offset + count, data + offset, m_size - offset); // empty out hole
if (count == 1) Traits::assign(data + offset, count, ch);
{
Traits::assign(*(data + offset), ch);
}
else
{
Traits::assign(data + offset, count, ch);
}
m_size = static_cast<internal_size_type>(num); m_size = static_cast<internal_size_type>(num);
Traits::assign(data[num], Element()); // terminate Traits::assign(data[num], Element()); // terminate
} }
@@ -582,14 +619,51 @@ namespace AZStd
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert(const_iterator insertPos, inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert(const_iterator insertPos,
InputIt first, InputIt last)-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator> InputIt first, InputIt last)-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>
{ // insert [_First, _Last) at _Where { // 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<InputIt>
&& is_same_v<typename AZStd::iterator_traits<InputIt>::value_type, value_type>)
{
insert(insertOffset, AZStd::to_address(first), AZStd::distance(first, last));
}
else if constexpr (Internal::is_forward_iterator_v<InputIt>)
{
// 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<Element>(*first));
}
m_size = static_cast<internal_size_type>(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<Element>(*first));
}
insert(insertOffset, inputCopy.c_str(), inputCopy.size());
}
return begin() + insertOffset;
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert(const_iterator insertPos, inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert(const_iterator insertPos,
AZStd::initializer_list<Element> ilist) -> iterator AZStd::initializer_list<Element> ilist) -> iterator
{ // insert [_First, _Last) at _Where { // insert [_First, _Last) at _Where
return insert_iter(insertPos, ilist.begin(), ilist.end()); return insert(insertPos, ilist.begin(), ilist.end());
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
@@ -604,7 +678,7 @@ namespace AZStd
{ {
// move elements down // move elements down
pointer data = m_buffer; 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<internal_size_type>(m_size - count); m_size = static_cast<internal_size_type>(m_size - count);
Traits::assign(data[m_size], Element()); // terminate Traits::assign(data[m_size], Element()); // terminate
} }
@@ -643,7 +717,7 @@ namespace AZStd
const basic_fixed_string& rhs) -> basic_fixed_string& const basic_fixed_string& rhs) -> basic_fixed_string&
{ {
// replace [offset, offset + count) with rhs // 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<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
@@ -651,56 +725,7 @@ namespace AZStd
const basic_fixed_string& rhs, size_type rhsOffset, size_type rhsCount) -> basic_fixed_string& const basic_fixed_string& rhs, size_type rhsOffset, size_type rhsCount) -> basic_fixed_string&
{ {
// replace [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount) // replace [offset, offset + count) with rhs [rhsOffset, rhsOffset + rhsCount)
AZSTD_CONTAINER_ASSERT(m_size >= offset && rhs.m_size >= rhsOffset, "Invalid offsets"); return replace(offset, count, rhs.c_str() + rhsOffset, AZStd::min(rhsCount, rhs.size() - rhsOffset));
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<internal_size_type>(newSize);
Traits::assign(data[newSize], Element()); // terminate
}
return *this;
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
template<typename T> template<typename T>
@@ -720,35 +745,83 @@ namespace AZStd
pointer data = m_buffer; pointer data = m_buffer;
// replace [offset, offset + count) with [ptr, ptr + ptrCount) // replace [offset, offset + count) with [ptr, ptr + ptrCount)
AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset"); AZSTD_CONTAINER_ASSERT(m_size >= offset, "Invalid offset");
if (m_size - offset < count) // Make sure count is within is no larger than the distance from the offset
{ // to the end of this string
count = m_size - offset; // trim _N0 to size count = AZStd::min(count, m_size - offset);
}
AZSTD_CONTAINER_ASSERT(npos - ptrCount > m_size - count, "Result too long");
size_type nm = m_size - count - offset; size_type newSize = m_size + ptrCount - count;
if (ptrCount < count) if (fits_in_capacity(newSize))
{ {
Traits::copy(data + offset + ptrCount, data + offset + count, nm); // smaller hole, move tail up // The code assumes that compile time evaluation will not need to deal with overlapping input
} size_type charsAfterCountToMove = m_size - count - offset;
size_type num = m_size + ptrCount - count; if (az_builtin_is_constant_evaluated() || !((ptr >= data + offset && ptr < data + offset + count)
if ((0 != ptrCount || 0 != count) && fits_in_capacity(num)) || (ptr + ptrCount > data + offset && ptr + ptrCount <= data + offset + count)))
{
data = m_buffer;
// make room and rearrange
if (count < ptrCount)
{ {
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);
}
} }
else
if (ptrCount > 0)
{ {
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<internal_size_type>(num);
Traits::assign(data[num], Element()); // terminate
} }
m_size = static_cast<internal_size_type>(newSize);
Traits::assign(data[newSize], Element()); // terminate
return *this; return *this;
} }
@@ -793,14 +866,7 @@ namespace AZStd
{ {
Traits::copy_backward(data + offset + num, data + offset + count, nm); // move tail down Traits::copy_backward(data + offset + num, data + offset + count, nm); // move tail down
} }
if (count == 1) Traits::assign(data + offset, num, ch);
{
Traits::assign(*(data + offset), ch);
}
else
{
Traits::assign(data + offset, num, ch);
}
m_size = static_cast<internal_size_type>(numToGrow); m_size = static_cast<internal_size_type>(numToGrow);
Traits::assign(data[numToGrow], Element()); // terminate Traits::assign(data[numToGrow], Element()); // terminate
} }
@@ -851,15 +917,54 @@ namespace AZStd
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt> template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::replace(const_iterator first, const_iterator last, inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::replace(const_iterator first, const_iterator last,
InputIt first2, InputIt last2) -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&> InputIt replaceFirst, InputIt replaceLast) -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{ // replace [first, last) with [first2,last2) { // replace [first, last) with [replaceFirst,replaceLast)
return replace_iter(first, last, first2, last2); if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
&& is_same_v<typename AZStd::iterator_traits<InputIt>::value_type, value_type>)
{
return replace(first, last, AZStd::to_address(replaceFirst), AZStd::distance(replaceFirst, replaceLast));
}
else if constexpr (Internal::is_forward_iterator_v<InputIt>)
{
// 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<Element>(*replaceFirst));
}
m_size = static_cast<internal_size_type>(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<Element>(*replaceFirst));
}
return replace(first, last, inputCopy.c_str(), inputCopy.size());
}
} }
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::replace(const_iterator first, const_iterator last, inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::replace(const_iterator first, const_iterator last,
AZStd::initializer_list<Element> ilist) -> basic_fixed_string& AZStd::initializer_list<Element> 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<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
@@ -1411,54 +1516,6 @@ namespace AZStd
return result; return result;
} }
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::construct_iter(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>>
{
// initialize from [first, last), input iterators
for (; first != last; ++first)
{
append((size_type)1, (Element)* first);
}
}
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::append_iter(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{ // append [first, last), input iterators
return replace(end(), end(), first, last);
}
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign_iter(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{
return replace(begin(), end(), first, last);
}
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert_iter(const_iterator insertPos, InputIt first,
InputIt last) -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>
{ // insert [first, last) at insertPos, input iterators
difference_type offset = insertPos - cbegin();
replace(insertPos, insertPos, first, last);
return iterator(m_buffer + offset);
}
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::replace_iter(const_iterator first, const_iterator last,
InputIt first2, InputIt last2) -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{ // replace [first, last) with [first2, last2), input iterators
basic_fixed_string rhs(first2, last2);
replace(first, last, rhs);
return *this;
}
template<class Element, size_t MaxElementCount, class Traits> template<class Element, size_t MaxElementCount, class Traits>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::fits_in_capacity(size_type newSize)-> bool inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::fits_in_capacity(size_type newSize)-> bool
{ {
File diff suppressed because it is too large Load Diff
@@ -280,18 +280,36 @@ namespace AZStd
static constexpr char_type* assign(char_type* dest, size_t count, char_type ch) noexcept static constexpr char_type* assign(char_type* dest, size_t count, char_type ch) noexcept
{ {
AZ_Assert(dest, "Invalid input!"); AZ_Assert(dest, "Invalid input!");
for (char_type* iter = dest; count; --count, ++iter)
if constexpr (AZStd::is_same_v<char_type, char>)
{ {
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; return dest;
} }
static constexpr bool eq(char_type left, char_type right) noexcept { return left == right; } 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 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 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<char_type, char>) if constexpr (AZStd::is_same_v<char_type, char>)
{ {
return __builtin_memcmp(s1, s2, count); return __builtin_memcmp(s1, s2, count);
@@ -301,7 +319,6 @@ namespace AZStd
return __builtin_wmemcmp(s1, s2, count); return __builtin_wmemcmp(s1, s2, count);
} }
else else
#endif
{ {
for (; count; --count, ++s1, ++s2) 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 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<char_type, char>) if constexpr (AZStd::is_same_v<char_type, char>)
{ {
return __builtin_char_memchr(s, ch, count); return __builtin_char_memchr(s, ch, count);
@@ -353,7 +366,6 @@ namespace AZStd
} }
else else
#endif
{ {
for (; count; --count, ++s) 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 static constexpr char_type* move(char_type* dest, const char_type* src, size_t count) noexcept
{ {
AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!"); AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!");
if (count == 0) if (count == 0 || src == dest)
{ {
return dest; return dest;
} }
char_type* result = dest;
// The less than(<), greater than(>) and other variants(<=, >=) #if az_has_builtin_memmove
// Cannot be compare pointers within a constexpr due to the potential for undefined behavior __builtin_memmove(dest, src, count * sizeof(char_type));
// per the bullet linked in the C++ standard at http://eel.is/c++draft/expr.compound#expr.rel-5 #else
// Now clang and gcc compilers allow the use of this relation operators in a constexpr, but auto NonBuiltinMove = [](char_type* dest1, const char_type* src1, size_t count1) constexpr
// msvc is not so forgiving -> char_type*
// 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 }; if (az_builtin_is_constant_evaluated())
for(const char_type* src_iter = src2; src_iter != src2 + count2; ++src_iter)
{ {
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
{ {
dest_less_than_src = false; bool dest_less_than_src{ true };
break; 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))
{
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));
}
return dest1;
}; };
NonBuiltinMove(dest, src, count);
#endif
if (should_copy_forward(dest, src, count)) return dest;
{
copy(dest, src, count);
}
else
{
copy_backward(dest, src, count);
}
return result;
} }
static constexpr char_type* copy(char_type* dest, const char_type* src, size_t count) noexcept static constexpr char_type* copy(char_type* dest, const char_type* src, size_t count) noexcept
{ {
AZ_Assert(dest != nullptr && src != nullptr, "Invalid input!"); 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); if (az_builtin_is_constant_evaluated())
} {
return result; 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 // 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. // 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; char_type* result = dest;
dest += count; #if az_has_builtin_memmove
src += count; __builtin_memmove(dest, src, count * sizeof(char_type));
for (; count; --count) #else
if (az_builtin_is_constant_evaluated())
{ {
assign(*--dest, *--src); dest += count;
src += count;
for (; count; --count)
{
assign(*--dest, *--src);
}
} }
else
{
::memmove(dest, src, count);
}
#endif
return result; return result;
} }
@@ -10,6 +10,13 @@
</Expand> </Expand>
</Type> </Type>
<Type Name="AZStd::compressed_pair_element&lt;*,*,*&gt;">
<DisplayString>{m_element}</DisplayString>
</Type>
<Type Name="AZStd::compressed_pair_element&lt;*,*,1&gt;">
<DisplayString>{$T1} is empty</DisplayString>
</Type>
<Type Name="AZStd::reverse_iterator&lt;*&gt;" Priority="Medium"> <Type Name="AZStd::reverse_iterator&lt;*&gt;" Priority="Medium">
<DisplayString>reverse_iterator base() {m_current}</DisplayString> <DisplayString>reverse_iterator base() {m_current}</DisplayString>
<Expand> <Expand>
@@ -388,35 +395,41 @@
<!-- --> <!-- -->
<Type Name="AZStd::basic_string&lt;char,*&gt;"> <Type Name="AZStd::basic_string&lt;char,*,*&gt;">
<DisplayString Condition="m_capacity &lt; SSO_BUF_SIZE">{m_buffer,s}</DisplayString> <DisplayString Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_buffer,s}</DisplayString>
<DisplayString Condition="m_capacity &gt;= SSO_BUF_SIZE">{m_data,s}</DisplayString> <DisplayString Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_data,s}</DisplayString>
<StringView Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer,s</StringView> <StringView Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_buffer,s</StringView>
<StringView Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data,s</StringView> <StringView Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_data,s</StringView>
<Expand> <Expand>
<Item Name="[size]">m_size</Item> <Item Name="[size]" Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">(size_t)((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_size</Item>
<Item Name="[capacity]">m_capacity</Item> <Item Name="[size]" Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_size</Item>
<Item Name="[capacity]" Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.Capacity</Item>
<Item Name="[capacity]" Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_capacity</Item>
<ArrayItems> <ArrayItems>
<Size>m_size</Size> <Size Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_size,u</Size>
<ValuePointer Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer</ValuePointer> <Size Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_size</Size>
<ValuePointer Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data</ValuePointer> <ValuePointer Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_buffer</ValuePointer>
<ValuePointer Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;char,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_data</ValuePointer>
</ArrayItems> </ArrayItems>
</Expand> </Expand>
</Type> </Type>
<Type Name="AZStd::basic_string&lt;wchar_t,*&gt;"> <Type Name="AZStd::basic_string&lt;wchar_t,*&gt;">
<AlternativeType Name="AZStd::basic_string&lt;unsigned short,*&gt;" /> <AlternativeType Name="AZStd::basic_string&lt;unsigned short,*&gt;" />
<DisplayString Condition="m_capacity &lt; SSO_BUF_SIZE">{m_buffer,su}</DisplayString> <DisplayString Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_buffer,su}</DisplayString>
<DisplayString Condition="m_capacity &gt;= SSO_BUF_SIZE">{m_data,su}</DisplayString> <DisplayString Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_data,su}</DisplayString>
<StringView Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer,su</StringView> <StringView Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_buffer,su</StringView>
<StringView Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data,su</StringView> <StringView Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_data,su</StringView>
<Expand> <Expand>
<Item Name="[size]">m_size</Item> <Item Name="[size]" Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">(size_t)((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_size</Item>
<Item Name="[capacity]">m_capacity</Item> <Item Name="[size]" Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_size</Item>
<Item Name="[capacity]" Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.Capacity</Item>
<Item Name="[capacity]" Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_capacity</Item>
<ArrayItems> <ArrayItems>
<Size>m_size</Size> <Size Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_size,u</Size>
<ValuePointer Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer</ValuePointer> <Size Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_size</Size>
<ValuePointer Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data</ValuePointer> <ValuePointer Condition="((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_buffer</ValuePointer>
<ValuePointer Condition="!((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element&lt;AZStd::basic_string&lt;wchar_t,$T1,$T2&gt;::Storage,0,0&gt;&amp;)m_storage).m_element.m_allocatedData.m_data</ValuePointer>
</ArrayItems> </ArrayItems>
</Expand> </Expand>
</Type> </Type>
File diff suppressed because it is too large Load Diff
@@ -72,12 +72,11 @@ namespace AWSCoreUnitTest
AWSCore::RequestBuilder requestBuilder{}; AWSCore::RequestBuilder requestBuilder{};
EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder));
std::shared_ptr<Aws::StringStream> bodyContent = requestBuilder.GetBodyContent(); std::shared_ptr<Aws::StringStream> bodyContent = requestBuilder.GetBodyContent();
EXPECT_TRUE(bodyContent != nullptr); EXPECT_NE(nullptr, bodyContent);
AZStd::string bodyString;
std::istreambuf_iterator<AZStd::string::value_type> eos; std::istreambuf_iterator<AZStd::string::value_type> eos;
bodyString = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(*bodyContent), eos }; AZStd::string bodyString{ std::istreambuf_iterator<AZStd::string::value_type>(*bodyContent), eos };
AZ_Printf("AWSAttributionServiceApiTest", bodyString.c_str()); AZ_Printf("AWSAttributionServiceApiTest", "%s", bodyString.c_str());
EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion)) != AZStd::string::npos); EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion)));
} }
} }
@@ -100,11 +100,10 @@ namespace AWSMetrics
AWSCore::RequestBuilder requestBuilder{}; AWSCore::RequestBuilder requestBuilder{};
EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder)); EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder));
std::shared_ptr<Aws::StringStream> bodyContent = requestBuilder.GetBodyContent(); std::shared_ptr<Aws::StringStream> bodyContent = requestBuilder.GetBodyContent();
EXPECT_TRUE(bodyContent != nullptr); ASSERT_NE(nullptr, bodyContent);
AZStd::string bodyString;
std::istreambuf_iterator<AZStd::string::value_type> eos; std::istreambuf_iterator<AZStd::string::value_type> eos;
bodyString = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(*bodyContent), eos }; AZStd::string bodyString{ std::istreambuf_iterator<AZStd::string::value_type>(*bodyContent), eos };
EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents)) != AZStd::string::npos); EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents)));
} }
} }
@@ -49,7 +49,7 @@ namespace EMotionFX
for (int i = 0; i < params.m_numStates; ++i) for (int i = 0; i < params.m_numStates; ++i)
{ {
AnimGraphNode* state = aznew AnimGraphMotionNode(); AnimGraphNode* state = aznew AnimGraphMotionNode();
state->SetName(AZStd::string(1, startChar + i).c_str()); state->SetName(AZStd::string(1, static_cast<char>(startChar + i)).c_str());
m_rootStateMachine->AddChildNode(state); m_rootStateMachine->AddChildNode(state);
AddTransitionWithTimeCondition(prevState, state, /*blendTime*/params.m_transitionBlendTime, /*countDownTime*/params.m_conditionCountDownTime); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/params.m_transitionBlendTime, /*countDownTime*/params.m_conditionCountDownTime);
prevState = state; prevState = state;
@@ -90,7 +90,7 @@ namespace EMotionFX
for (int i = 0; i < param.m_numStates; ++i) for (int i = 0; i < param.m_numStates; ++i)
{ {
AnimGraphBindPoseNode* state = aznew AnimGraphBindPoseNode(); AnimGraphBindPoseNode* state = aznew AnimGraphBindPoseNode();
state->SetName(AZStd::string(1, startChar + i).c_str()); state->SetName(AZStd::string(1, static_cast<char>(startChar + i)).c_str());
m_rootStateMachine->AddChildNode(state); m_rootStateMachine->AddChildNode(state);
AddTransitionWithTimeCondition(prevState, state, /*blendTime*/param.m_blendTime, /*countDownTime*/param.m_countDownTime); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/param.m_blendTime, /*countDownTime*/param.m_countDownTime);
prevState = state; prevState = state;