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>
monroegm-disable-blank-issue-2
lumberyard-employee-dm 4 years ago committed by GitHub
parent 7c88f20e1e
commit 098005afbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -149,3 +149,79 @@
#if !defined(AZ_COMMAND_LINE_LEN)
# define AZ_COMMAND_LINE_LEN 2048
#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
{
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

@ -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);

@ -8,6 +8,7 @@
#pragma once
#include <AzCore/std/utils.h>
#include <AzCore/std/typetraits/is_constructible.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
@ -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 <class T> compressed_pair(skip_element_tag, T&&)"
// constructor below, the default constructor template types needs to be distinguished from it
template <typename = void, typename = AZStd::enable_if_t<
AZStd::is_default_constructible<first_base_value_type>::value
&& AZStd::is_default_constructible<second_base_value_type>::value>>
// First template argument is used to perform a substitution into AZStd::enable_if_t
// so that SFINAE can trigger
template <typename Unused = void, typename = AZStd::enable_if_t<
AZStd::is_default_constructible_v<first_base_value_type>
&& AZStd::is_default_constructible_v<second_base_value_type>, Unused>>
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);
template <typename T>

@ -75,7 +75,7 @@ namespace AZStd
}
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)
: first_base_type{ AZStd::forward<T>(firstElement) }
, second_base_type{}
@ -117,7 +117,7 @@ namespace AZStd
{
return static_cast<const first_base_type&>(*this).get();
}
template <typename T1, typename T2>
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, ...);
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;
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>
inline constexpr basic_fixed_string<Element, MaxElementCount, Traits>::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<typename T, typename>
inline constexpr basic_fixed_string<Element, MaxElementCount, Traits>::basic_fixed_string(const T& convertibleToView)
{
AZStd::basic_string_view<Element, Traits> 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<internal_size_type>(num);
Traits::assign(data[num], Element()); // terminate
}
@ -332,13 +316,47 @@ namespace AZStd
template<class InputIt>
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&>
{ // 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>
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>
@ -420,18 +438,10 @@ namespace AZStd
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::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<internal_size_type>(count);
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)
-> 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>
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>
@ -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<internal_size_type>(num);
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,
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
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>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert(const_iterator insertPos,
AZStd::initializer_list<Element> ilist) -> iterator
{ // 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>
@ -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<internal_size_type>(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<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&
{
// 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<internal_size_type>(newSize);
Traits::assign(data[newSize], Element()); // terminate
}
return *this;
return replace(offset, count, rhs.c_str() + rhsOffset, AZStd::min(rhsCount, rhs.size() - rhsOffset));
}
template<class Element, size_t MaxElementCount, class Traits>
template<typename T>
@ -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<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;
}
@ -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<internal_size_type>(numToGrow);
Traits::assign(data[numToGrow], Element()); // terminate
}
@ -851,15 +917,54 @@ namespace AZStd
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
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&>
{ // replace [first, last) with [first2,last2)
return replace_iter(first, last, first2, last2);
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 [replaceFirst,replaceLast)
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>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::replace(const_iterator first, const_iterator last,
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>
@ -1411,54 +1516,6 @@ namespace AZStd
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>
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
{
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;
}
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<char_type, char>)
{
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<char_type, char>)
{
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;
}

@ -10,6 +10,13 @@
</Expand>
</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">
<DisplayString>reverse_iterator base() {m_current}</DisplayString>
<Expand>
@ -388,35 +395,41 @@
<!-- -->
<Type Name="AZStd::basic_string&lt;char,*&gt;">
<DisplayString Condition="m_capacity &lt; SSO_BUF_SIZE">{m_buffer,s}</DisplayString>
<DisplayString Condition="m_capacity &gt;= SSO_BUF_SIZE">{m_data,s}</DisplayString>
<StringView Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer,s</StringView>
<StringView Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data,s</StringView>
<Type Name="AZStd::basic_string&lt;char,*,*&gt;">
<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="!((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="((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="!((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>
<Item Name="[size]">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">(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="[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>
<Size>m_size</Size>
<ValuePointer Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer</ValuePointer>
<ValuePointer Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data</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_shortData.m_size,u</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_allocatedData.m_size</Size>
<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>
</Expand>
</Type>
<Type Name="AZStd::basic_string&lt;wchar_t,*&gt;">
<AlternativeType Name="AZStd::basic_string&lt;unsigned short,*&gt;" />
<DisplayString Condition="m_capacity &lt; SSO_BUF_SIZE">{m_buffer,su}</DisplayString>
<DisplayString Condition="m_capacity &gt;= SSO_BUF_SIZE">{m_data,su}</DisplayString>
<StringView Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer,su</StringView>
<StringView Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data,su</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<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="!((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="((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="!((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>
<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="[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>
<Size>m_size</Size>
<ValuePointer Condition="m_capacity &lt; SSO_BUF_SIZE">m_buffer</ValuePointer>
<ValuePointer Condition="m_capacity &gt;= SSO_BUF_SIZE">m_data</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_shortData.m_size,u</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_allocatedData.m_size</Size>
<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>
</Expand>
</Type>

File diff suppressed because it is too large Load Diff

@ -72,12 +72,11 @@ namespace AWSCoreUnitTest
AWSCore::RequestBuilder requestBuilder{};
EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder));
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;
bodyString = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(*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<AZStd::string::value_type>(*bodyContent), eos };
AZ_Printf("AWSAttributionServiceApiTest", "%s", bodyString.c_str());
EXPECT_TRUE(bodyString.contains(AZStd::string::format("{\"%s\":\"1.1\"", AwsAttributionAttributeKeyVersion)));
}
}

@ -100,11 +100,10 @@ namespace AWSMetrics
AWSCore::RequestBuilder requestBuilder{};
EXPECT_TRUE(request.parameters.BuildRequest(requestBuilder));
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;
bodyString = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(*bodyContent), eos };
EXPECT_TRUE(bodyString.find(AZStd::string::format("{\"%s\":[{\"event_timestamp\":", AwsMetricsRequestParameterKeyEvents)) != AZStd::string::npos);
AZStd::string bodyString{ std::istreambuf_iterator<AZStd::string::value_type>(*bodyContent), eos };
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)
{
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);
AddTransitionWithTimeCondition(prevState, state, /*blendTime*/params.m_transitionBlendTime, /*countDownTime*/params.m_conditionCountDownTime);
prevState = state;

@ -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<char>(startChar + i)).c_str());
m_rootStateMachine->AddChildNode(state);
AddTransitionWithTimeCondition(prevState, state, /*blendTime*/param.m_blendTime, /*countDownTime*/param.m_countDownTime);
prevState = state;

Loading…
Cancel
Save