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:
committed by
GitHub
parent
7c88f20e1e
commit
098005afbc
@@ -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)
|
||||
size_type newSize = m_size + ptrCount - count;
|
||||
if (fits_in_capacity(newSize))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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
|
||||
|
||||
#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*
|
||||
{
|
||||
bool dest_less_than_src{ true };
|
||||
for(const char_type* src_iter = src2; src_iter != src2 + count2; ++src_iter)
|
||||
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
|
||||
{
|
||||
dest_less_than_src = false;
|
||||
break;
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
copy(dest, src, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_backward(dest, src, count);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
assign(*--dest, *--src);
|
||||
dest += count;
|
||||
src += count;
|
||||
for (; count; --count)
|
||||
{
|
||||
assign(*--dest, *--src);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
::memmove(dest, src, count);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="AZStd::compressed_pair_element<*,*,*>">
|
||||
<DisplayString>{m_element}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="AZStd::compressed_pair_element<*,*,1>">
|
||||
<DisplayString>{$T1} is empty</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="AZStd::reverse_iterator<*>" Priority="Medium">
|
||||
<DisplayString>reverse_iterator base() {m_current}</DisplayString>
|
||||
<Expand>
|
||||
@@ -388,35 +395,41 @@
|
||||
|
||||
<!-- -->
|
||||
|
||||
<Type Name="AZStd::basic_string<char,*>">
|
||||
<DisplayString Condition="m_capacity < SSO_BUF_SIZE">{m_buffer,s}</DisplayString>
|
||||
<DisplayString Condition="m_capacity >= SSO_BUF_SIZE">{m_data,s}</DisplayString>
|
||||
<StringView Condition="m_capacity < SSO_BUF_SIZE">m_buffer,s</StringView>
|
||||
<StringView Condition="m_capacity >= SSO_BUF_SIZE">m_data,s</StringView>
|
||||
<Type Name="AZStd::basic_string<char,*,*>">
|
||||
<DisplayString Condition="((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,s}</DisplayString>
|
||||
<DisplayString Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,s}</DisplayString>
|
||||
<StringView Condition="((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,s</StringView>
|
||||
<StringView Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)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<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">(size_t)((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size</Item>
|
||||
<Item Name="[size]" Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size</Item>
|
||||
<Item Name="[capacity]" Condition="((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.Capacity</Item>
|
||||
<Item Name="[capacity]" Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_capacity</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer Condition="m_capacity < SSO_BUF_SIZE">m_buffer</ValuePointer>
|
||||
<ValuePointer Condition="m_capacity >= SSO_BUF_SIZE">m_data</ValuePointer>
|
||||
<Size Condition="((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size,u</Size>
|
||||
<Size Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size</Size>
|
||||
<ValuePointer Condition="((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer</ValuePointer>
|
||||
<ValuePointer Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<char,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="AZStd::basic_string<wchar_t,*>">
|
||||
<AlternativeType Name="AZStd::basic_string<unsigned short,*>" />
|
||||
<DisplayString Condition="m_capacity < SSO_BUF_SIZE">{m_buffer,su}</DisplayString>
|
||||
<DisplayString Condition="m_capacity >= SSO_BUF_SIZE">{m_data,su}</DisplayString>
|
||||
<StringView Condition="m_capacity < SSO_BUF_SIZE">m_buffer,su</StringView>
|
||||
<StringView Condition="m_capacity >= SSO_BUF_SIZE">m_data,su</StringView>
|
||||
<DisplayString Condition="((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,su}</DisplayString>
|
||||
<DisplayString Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">{((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,su}</DisplayString>
|
||||
<StringView Condition="((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer,su</StringView>
|
||||
<StringView Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data,su</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<Item Name="[capacity]">m_capacity</Item>
|
||||
<Item Name="[size]" Condition="((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">(size_t)((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size</Item>
|
||||
<Item Name="[size]" Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size</Item>
|
||||
<Item Name="[capacity]" Condition="((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.Capacity</Item>
|
||||
<Item Name="[capacity]" Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_capacity</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer Condition="m_capacity < SSO_BUF_SIZE">m_buffer</ValuePointer>
|
||||
<ValuePointer Condition="m_capacity >= SSO_BUF_SIZE">m_data</ValuePointer>
|
||||
<Size Condition="((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_size,u</Size>
|
||||
<Size Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_size</Size>
|
||||
<ValuePointer Condition="((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_buffer</ValuePointer>
|
||||
<ValuePointer Condition="!((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_shortData.m_ssoActive">((AZStd::compressed_pair_element<AZStd::basic_string<wchar_t,$T1,$T2>::Storage,0,0>&)m_storage).m_element.m_allocatedData.m_data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user