Merge branch 'development' into Prefabs/PlayInEditorMissingAssets

This commit is contained in:
AMZN-koppersr
2022-01-27 09:51:06 -08:00
1261 changed files with 15258 additions and 89931 deletions
@@ -13,7 +13,6 @@ set(FILES
Instance/InstanceData.h
Instance/InstanceData.cpp
Instance/InstanceDatabase.h
std/containers/array_view.h
std/containers/fixed_vector_set.h
std/containers/lru_cache.h
std/containers/vector_set.h
@@ -1,156 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/array.h>
namespace AZStd
{
/**
* Immutable wrapper for an array of data. It does not maintain storage for the data,
* but just holds pointers to mark the beginning and end of the array. It can be
* conveniently constructed from a variety of other container types like array,
* vector, and fixed_vector.
*
* Example:
* Given "void Func(AZStd::array_view<int> a) {...}" you can call...
* - Func({1,2,3});
* - AZStd::array<int,3> a = {1,2,3};
* Func(a);
* - AZStd::vector<int> v = {1,2,3};
* Func(v);
* - AZStd::fixed_vector<int,10> fv = {1,2,3};
* Func(fv);
*
* Since the array_view does not copy and store any data, it is only valid as long as the data used to create it is valid.
*/
template <class Element>
class array_view final
{
public:
using value_type = Element;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = AZStd::size_t;
using difference_type = AZStd::ptrdiff_t;
using iterator = const value_type*;
using const_iterator = const value_type*;
using reverse_iterator = AZStd::reverse_iterator<iterator>;
using const_reverse_iterator = AZStd::reverse_iterator<const_iterator>;
array_view()
: m_begin(nullptr)
, m_end(nullptr)
{ }
~array_view() = default;
array_view(const_pointer s, size_type length)
: m_begin(s)
, m_end(m_begin + length)
{
if (length == 0) erase();
}
array_view(const_pointer first, const_pointer last)
: m_begin(first)
, m_end(last)
{ }
// We explicitly delete this constructor because it's too easy to accidentally
// create an array_view to just the first element instead of an entire array.
array_view(const_pointer s) = delete;
template<AZStd::size_t N>
array_view(const AZStd::array<value_type, N>& data)
: m_begin(data.data())
, m_end(m_begin + data.size())
{ }
array_view(const AZStd::vector<value_type>& data)
: m_begin(data.data())
, m_end(m_begin + data.size())
{ }
template<AZStd::size_t N>
array_view(const AZStd::fixed_vector<value_type, N>& data)
: m_begin(data.data())
, m_end(m_begin + data.size())
{ }
array_view(const array_view&) = default;
array_view(array_view&& other)
: array_view(other.m_begin, other.m_end)
{
#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging
other.m_begin = nullptr;
other.m_end = nullptr;
#endif
}
array_view& operator=(const array_view& other) = default;
array_view& operator=(array_view&& other)
{
m_begin = other.m_begin;
m_end = other.m_end;
#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging
other.m_begin = nullptr;
other.m_end = nullptr;
#endif
return *this;
}
size_type size() const { return m_end - m_begin; }
bool empty() const { return m_end == m_begin; }
const_pointer data() const { return m_begin; }
const_reference operator[](size_type index) const
{
AZ_Assert(index < size(), "index value is out of range");
return m_begin[index];
}
void erase() { m_begin = m_end = nullptr; }
iterator begin() const { return m_begin; }
iterator end() const { return m_end; }
const_iterator cbegin() const { return m_begin; }
const_iterator cend() const { return m_end; }
reverse_iterator rbegin() const { return reverse_iterator(m_end); }
reverse_iterator rend() const { return reverse_iterator(m_begin); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
friend bool operator==(array_view lhs, array_view rhs)
{
return lhs.m_begin == rhs.m_begin && lhs.m_end == rhs.m_end;
}
friend bool operator!=(array_view lhs, array_view rhs) { return !(lhs == rhs); }
friend bool operator< (array_view lhs, array_view rhs) { return lhs.m_begin < rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end < rhs.m_end; }
friend bool operator> (array_view lhs, array_view rhs) { return lhs.m_begin > rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end > rhs.m_end; }
friend bool operator<=(array_view lhs, array_view rhs) { return lhs == rhs || lhs < rhs; }
friend bool operator>=(array_view lhs, array_view rhs) { return lhs == rhs || lhs > rhs; }
private:
const_pointer m_begin;
const_pointer m_end;
};
} // namespace AZStd
-300
View File
@@ -1,300 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AtomCore/std/containers/array_view.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
using namespace AZStd;
class ArrayView : public AllocatorsTestFixture
{
protected:
template<typename T>
void ExpectEqual(initializer_list<T> expectedValues, array_view<T> arrayView)
{
EXPECT_EQ(false, arrayView.empty());
EXPECT_EQ(expectedValues.size(), arrayView.size());
typename AZStd::vector<T>::const_iterator iterator = arrayView.begin();
for (int i = 0; i < expectedValues.size(); ++i, ++iterator)
{
EXPECT_EQ(expectedValues.begin()[i], arrayView[i]);
EXPECT_EQ(expectedValues.begin()[i], *iterator);
}
EXPECT_EQ(iterator, arrayView.end());
}
};
TEST_F(ArrayView, DefaultConstructor)
{
array_view<bool> defaultView;
EXPECT_EQ(nullptr, defaultView.begin());
EXPECT_EQ(nullptr, defaultView.end());
EXPECT_EQ(0, defaultView.size());
EXPECT_EQ(true, defaultView.empty());
}
TEST_F(ArrayView, PointerConstructor1)
{
int originalValues[4] = { 2,3,4,5 };
array_view<int> view(originalValues, AZ_ARRAY_SIZE(originalValues));
ExpectEqual({ 2,3,4,5 }, view);
EXPECT_EQ(originalValues, view.begin());
EXPECT_EQ(&originalValues[4], view.end());
}
TEST_F(ArrayView, PointerConstructor2)
{
int originalValues[3] = { 6,7,8 };
array_view<int> view(originalValues, &originalValues[3]);
ExpectEqual({ 6,7,8 }, view);
EXPECT_EQ(originalValues, view.begin());
EXPECT_EQ(&originalValues[3], view.end());
}
TEST_F(ArrayView, ArrayConstructor)
{
array<int, 4> originalValues = { 9,10,11,12 };
array_view<int> view(originalValues);
ExpectEqual({ 9,10,11,12 }, view);
EXPECT_EQ(originalValues.begin(), view.begin());
EXPECT_EQ(originalValues.end(), view.end());
}
TEST_F(ArrayView, VectorConstructor)
{
vector<int> originalValues = { 13,14,15,16,17,18 };
array_view<int> view(originalValues);
ExpectEqual({ 13,14,15,16,17,18 }, view);
EXPECT_EQ(originalValues.begin(), view.begin());
EXPECT_EQ(originalValues.end(), view.end());
}
TEST_F(ArrayView, FixedVectorConstructor)
{
fixed_vector<int, 10> originalValues = { 17,18,19 }; // Note that even though the fixed_vector capacity is 10, it's size is 3, so the view size will be 3 as well
array_view<int> view(originalValues);
ExpectEqual({ 17,18,19 }, view);
EXPECT_EQ(originalValues.begin(), view.begin());
EXPECT_EQ(originalValues.end(), view.end());
}
TEST_F(ArrayView, CopyConstructor)
{
fixed_vector<int, 2> originalValues = { 27,28 };
array_view<int> view1(originalValues);
array_view<int> view2(view1);
ExpectEqual({ 27,28 }, view2);
EXPECT_EQ(view1.begin(), view2.begin());
EXPECT_EQ(view1.end(), view2.end());
}
TEST_F(ArrayView, MoveConstructor)
{
int originalValues[] = { 29,30,31 };
array_view<int> view1(originalValues, AZ_ARRAY_SIZE(originalValues));
array_view<int> view2(AZStd::move(view1));
ExpectEqual({ 29,30,31 }, view2);
EXPECT_EQ(originalValues, view2.begin());
EXPECT_EQ(&originalValues[3], view2.end());
// This isn't strictly necessary but is a good way to make sure the move
// constructor actually exists and it itn't just calling the copy constructor
#if AZ_DEBUG_BUILD // The pointers are only cleared in debug
EXPECT_EQ(nullptr, view1.begin());
EXPECT_EQ(nullptr, view1.end());
#endif
}
TEST_F(ArrayView, AssignmentOperator)
{
fixed_vector<int, 4> originalValues = { 32,33,34,35 };
array_view<int> view1(originalValues);
array_view<int> view2;
view2 = view1;
ExpectEqual({ 32,33,34,35 }, view2);
EXPECT_EQ(view1.begin(), view2.begin());
EXPECT_EQ(view1.end(), view2.end());
}
TEST_F(ArrayView, MoveAssignmentOperator)
{
int originalValues[] = { 36,37,38,39,40 };
array_view<int> view1(originalValues, AZ_ARRAY_SIZE(originalValues));
array_view<int> view2;
view2 = AZStd::move(view1);
ExpectEqual({ 36,37,38,39,40 }, view2);
EXPECT_EQ(originalValues, view2.begin());
EXPECT_EQ(&originalValues[5], view2.end());
// This isn't strictly necessary but is a good way to make sure the move
// assignment operator actually exists and it itn't just calling the norm
// assignment operator
#if AZ_DEBUG_BUILD // The pointers are only cleared in debug
EXPECT_EQ(nullptr, view1.begin());
EXPECT_EQ(nullptr, view1.end());
#endif
}
TEST_F(ArrayView, Erase)
{
fixed_vector<int, 4> originalValues = { 1,2,3,4 };
array_view<int> view(originalValues);
view.erase();
EXPECT_EQ(nullptr, view.begin());
EXPECT_EQ(nullptr, view.end());
EXPECT_EQ(0, view.size());
EXPECT_EQ(true, view.empty());
}
TEST_F(ArrayView, BeginAndEnd)
{
fixed_vector<int, 4> originalValues = { 1,2,3,4 };
array_view<int> view(originalValues);
EXPECT_EQ(1, view.begin()[0]);
EXPECT_EQ(4, view.end()[-1]);
EXPECT_EQ(1, view.cbegin()[0]);
EXPECT_EQ(4, view.cend()[-1]);
EXPECT_EQ(4, view.rbegin()[0]);
EXPECT_EQ(1, view.rend()[-1]);
EXPECT_EQ(4, view.crbegin()[0]);
EXPECT_EQ(1, view.crend()[-1]);
}
TEST_F(ArrayView, ImplicitConstruction)
{
// This test verifies that we can pass in various non-array_view types
// into functions that take an array_view
// The compile cannot detect the correct template type so that has to be specified explicitly
ExpectEqual<int>({ 1,2,3 }, vector<int>({ 1,2,3 }));
ExpectEqual<int>({ 1,2,3 }, fixed_vector<int, 3>({ 1,2,3 }));
ExpectEqual<int>({ 1,2,3 }, array<int, 3>({ 1,2,3 }));
}
void CheckComparisonOperators(bool areEqual, array_view<int> a, array_view<int> b)
{
EXPECT_EQ(areEqual, a == b);
// For less/greater operators, the exact order doesn't really matter;
// We just check for internal consistency
if (areEqual)
{
EXPECT_EQ(false, a != b);
EXPECT_EQ(false, a < b);
EXPECT_EQ(false, a > b);
EXPECT_EQ(true, a <= b);
EXPECT_EQ(true, a >= b);
}
else
{
EXPECT_EQ(true, a != b);
EXPECT_EQ(a > b, a >= b);
EXPECT_EQ(a < b, a <= b);
EXPECT_NE(a > b, a < b);
EXPECT_NE(a >= b, a <= b);
EXPECT_NE(a >= b, a < b);
EXPECT_NE(a > b, a <= b);
EXPECT_NE(a <= b, a > b);
EXPECT_NE(a < b, a >= b);
}
}
TEST_F(ArrayView, ComparisonOperators)
{
int arrayA[] = { 1,2,3 };
int arrayB[] = { 1,2,3 };
array_view<int> arrayA_view(arrayA, 3);
array_view<int> arrayB_view(arrayB, 3);
array_view<int> arrayA_otherView(arrayA, 3);
// view of a sub-array aligned to the beginning of the array
array_view<int> arrayA_headView(arrayA, 2);
array_view<int> arrayB_headView(arrayB, 2);
// view of a sub-array aligned to the end of the array
array_view<int> arrayA_tailView(&arrayA[1], 2);
array_view<int> arrayB_tailView(&arrayB[1], 2);
// view of a sub-array in the middle of the array
array_view<int> arrayA_centerView(&arrayA[1], 1);
array_view<int> arrayB_centerView(&arrayB[1], 1);
// Same view
CheckComparisonOperators(true, arrayA_view, arrayA_view);
// Different view, same array
CheckComparisonOperators(true, arrayA_view, arrayA_otherView);
CheckComparisonOperators(true, arrayA_otherView, arrayA_view);
// Different arrays
CheckComparisonOperators(false, arrayA_view, arrayB_view);
CheckComparisonOperators(false, arrayB_view, arrayA_view);
// Same arrays, but one is a just a subset of the array
CheckComparisonOperators(false, arrayA_view, arrayA_headView);
CheckComparisonOperators(false, arrayA_view, arrayA_tailView);
CheckComparisonOperators(false, arrayA_view, arrayA_centerView);
CheckComparisonOperators(false, arrayA_headView, arrayA_view);
CheckComparisonOperators(false, arrayA_tailView, arrayA_view);
CheckComparisonOperators(false, arrayA_centerView, arrayA_view);
// Different arrays, different lengths
CheckComparisonOperators(false, arrayA_view, arrayB_headView);
CheckComparisonOperators(false, arrayB_view, arrayA_headView);
CheckComparisonOperators(false, arrayB_headView, arrayA_view);
CheckComparisonOperators(false, arrayA_headView, arrayB_view);
}
TEST_F(ArrayView, AssertOutOfBounds)
{
array_view<int> view({ 1,2,3,4 });
AZ_TEST_START_TRACE_SUPPRESSION;
view[4];
view[5];
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
}
}
@@ -7,7 +7,6 @@
#
set(FILES
ArrayView.cpp
ConcurrencyCheckerTests.cpp
InstanceDatabase.cpp
lru_cache.cpp
@@ -64,7 +64,7 @@ public class LumberyardActivity extends NativeActivity
////////////////////////////////////////////////////////////////
// called from the native to get the application package name
// e.g. com.lumberyard.samples for SamplesProject
// e.g. org.o3de.samples for SamplesProject
public String GetPackageName()
{
return getApplicationContext().getPackageName();
-11
View File
@@ -1,11 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#define AZCORE_BUILD_NUMBER 368
#define AZCORE_BUILD_DATE "Thu 10/10/2013"
#define AZCORE_BUILD_TIME "19:42:16.96"
#define AZCORE_SOURCE_CHANGELIST 2992189
@@ -49,7 +49,7 @@ namespace AZ
return m_entity->GetId();
}
AZ_Warning("System", false, "Can't get component %p entity ID as it is not attached to an entity yet!", this);
AZ_Warning("System", false, "Can't get component (type: %s, addr: %p) entity ID as it is not attached to an entity yet!", RTTI_GetTypeName(), this);
return EntityId();
}
@@ -60,7 +60,7 @@ namespace AZ
return NamedEntityId(m_entity->GetId(), m_entity->GetName());
}
AZ_Warning("System", false, "Can't get component %p entity ID as it is not attached to an entity yet!", this);
AZ_Warning("System", false, "Can't get component (type: %s, addr: %p) entity ID as it is not attached to an entity yet!", RTTI_GetTypeName(), this);
return NamedEntityId();
}
@@ -152,8 +152,6 @@ namespace AZ
m_reservedDebug = 0;
m_recordingMode = Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE;
m_stackRecordLevels = 5;
m_useOverrunDetection = false;
m_useMalloc = false;
}
bool AppDescriptorConverter(SerializeContext& serialize, SerializeContext::DataElementNode& node)
@@ -323,9 +321,6 @@ namespace AZ
->Field("blockSize", &Descriptor::m_memoryBlocksByteSize)
->Field("reservedOS", &Descriptor::m_reservedOS)
->Field("reservedDebug", &Descriptor::m_reservedDebug)
->Field("useOverrunDetection", &Descriptor::m_useOverrunDetection)
->Field("useMalloc", &Descriptor::m_useMalloc)
->Field("allocatorRemappings", &Descriptor::m_allocatorRemappings)
->Field("modules", &Descriptor::m_modules)
;
@@ -361,8 +356,6 @@ namespace AZ
->Attribute(Edit::Attributes::Step, &Descriptor::m_pageSize)
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedOS, "OS reserved memory", "System memory reserved for OS (used only when 'Allocate all memory at startup' is true)")
->DataElement(Edit::UIHandlers::SpinBox, &Descriptor::m_reservedDebug, "Memory reserved for debugger", "System memory reserved for Debug allocator, like memory tracking (used only when 'Allocate all memory at startup' is true)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_useOverrunDetection, "Use Overrun Detection", "Use the overrun detection memory manager (only available on some platforms, ignored in Release builds)")
->DataElement(Edit::UIHandlers::CheckBox, &Descriptor::m_useMalloc, "Use Malloc", "Use malloc for memory allocations (for memory debugging only, ignored in Release builds)")
;
}
}
@@ -881,7 +874,7 @@ namespace AZ
AZ::AllocatorInstance<AZ::SystemAllocator>::Create(desc);
AZ::Debug::Trace::Instance().Init();
AZ::Debug::AllocationRecords* records = AllocatorInstance<SystemAllocator>::GetAllocator().GetRecords();
AZ::Debug::AllocationRecords* records = AllocatorInstance<SystemAllocator>::Get().GetRecords();
if (records)
{
records->SetMode(m_descriptor.m_recordingMode);
@@ -893,35 +886,6 @@ namespace AZ
m_isSystemAllocatorOwner = true;
}
#ifndef RELEASE
if (m_descriptor.m_useOverrunDetection)
{
OverrunDetectionSchema::Descriptor overrunDesc(false);
s_overrunDetectionSchema = Environment::CreateVariable<OverrunDetectionSchema>(AzTypeInfo<OverrunDetectionSchema>::Name(), overrunDesc);
OverrunDetectionSchema* schemaPtr = &s_overrunDetectionSchema.Get();
AZ::AllocatorManager::Instance().SetOverrideAllocatorSource(schemaPtr);
}
if (m_descriptor.m_useMalloc)
{
AZ_Printf("Malloc", "WARNING: Malloc override is enabled. Registered allocators will use malloc instead of their normal allocation schemas.");
s_mallocSchema = Environment::CreateVariable<MallocSchema>(AzTypeInfo<MallocSchema>::Name());
MallocSchema* schemaPtr = &s_mallocSchema.Get();
AZ::AllocatorManager::Instance().SetOverrideAllocatorSource(schemaPtr);
}
#endif
AllocatorManager& allocatorManager = AZ::AllocatorManager::Instance();
for (const auto& remapping : m_descriptor.m_allocatorRemappings)
{
allocatorManager.AddAllocatorRemapping(remapping.m_from.c_str(), remapping.m_to.c_str());
}
allocatorManager.FinalizeConfiguration();
}
void ComponentApplication::MergeSettingsToRegistry(SettingsRegistryInterface& registry)
@@ -142,10 +142,6 @@ namespace AZ
AZ::u64 m_reservedDebug; //!< Reserved memory for Debugging (allocation,etc.). Used only when m_grabAllMemory is set to true. (default: 0)
Debug::AllocationRecords::Mode m_recordingMode; //!< When to record stack traces (default: AZ::Debug::AllocationRecords::RECORD_STACK_IF_NO_FILE_LINE)
AZ::u64 m_stackRecordLevels; //!< If stack recording is enabled, how many stack levels to record. (default: 5)
bool m_useOverrunDetection; //!< True to use the overrun detection memory management scheme. Only available on some platforms; greatly increases memory consumption.
bool m_useMalloc; //!< True to use malloc instead of the internal memory manager. Intended for debugging purposes only.
AllocatorRemappings m_allocatorRemappings; //!< List of remappings of allocators to perform, so that they can alias each other.
ModuleDescriptorList m_modules; //!< Dynamic modules used by the application.
//!< These will be loaded on startup.
@@ -159,7 +155,7 @@ namespace AZ
//! If set, this allocator is used to allocate the temporary bootstrap memory, as well as the main \ref SystemAllocator heap.
//! If it's left nullptr (default), the \ref OSAllocator will be used.
IAllocatorAllocate* m_allocator = nullptr;
IAllocator* m_allocator = nullptr;
//! Callback to create AZ::Modules for the static libraries linked by this application.
//! Leave null if the application uses no static AZ::Modules.
@@ -372,7 +368,7 @@ namespace AZ
bool m_isOSAllocatorOwner{ false };
bool m_ownsConsole{};
void* m_fixedMemoryBlock{ nullptr }; //!< Pointer to the memory block allocator, so we can free it OnDestroy.
IAllocatorAllocate* m_osAllocator{ nullptr };
IAllocator* m_osAllocator{ nullptr };
EntitySetType m_entities;
AZ::SettingsRegistryInterface::NotifyEventHandler m_projectPathChangedHandler;
@@ -230,7 +230,7 @@ namespace AZ
EBUS_EVENT_ID(m_id, EntityBus, OnEntityDeactivated, m_id);
EBUS_EVENT(EntitySystemBus, OnEntityDeactivated, m_id);
AZ_Assert(m_state == State::Active, "Component should be in Active state to br Deactivated!");
AZ_Assert(m_state == State::Active, "Component should be in Active state to be Deactivated!");
SetState(State::Deactivating);
for (ComponentArrayType::reverse_iterator it = m_components.rbegin(); it != m_components.rend(); ++it)
@@ -15,7 +15,7 @@ struct z_stream_s;
namespace AZ
{
class IAllocator;
class IAllocatorAllocate;
class IAllocatorSchema;
/**
* The most well known and used compression algorithm. It gives the best compression ratios even on level 1,
@@ -90,7 +90,7 @@ namespace AZ
z_stream_s* m_strDeflate;
z_stream_s* m_strInflate;
IAllocatorAllocate* m_workMemoryAllocator;
IAllocatorSchema* m_workMemoryAllocator;
};
}
@@ -26,7 +26,7 @@ ZLib::ZLib(IAllocator* workMemAllocator)
: m_strDeflate(nullptr)
, m_strInflate(nullptr)
{
m_workMemoryAllocator = workMemAllocator ? workMemAllocator->GetAllocationSource() : nullptr;
m_workMemoryAllocator = workMemAllocator->GetSchema();
if (!m_workMemoryAllocator)
{
m_workMemoryAllocator = &AllocatorInstance<SystemAllocator>::Get();
@@ -55,7 +55,7 @@ ZLib::~ZLib()
//=========================================================================
void* ZLib::AllocateMem(void* userData, unsigned int items, unsigned int size)
{
IAllocatorAllocate* allocator = reinterpret_cast<IAllocatorAllocate*>(userData);
IAllocator* allocator = reinterpret_cast<IAllocator*>(userData);
return allocator->Allocate(items * size, 4, 0, "ZLib", __FILE__, __LINE__);
}
@@ -65,7 +65,7 @@ void* ZLib::AllocateMem(void* userData, unsigned int items, unsigned int size)
//=========================================================================
void ZLib::FreeMem(void* userData, void* address)
{
IAllocatorAllocate* allocator = reinterpret_cast<IAllocatorAllocate*>(userData);
IAllocator* allocator = reinterpret_cast<IAllocator*>(userData);
allocator->DeAllocate(address);
}
@@ -16,7 +16,7 @@
using namespace AZ;
ZStd::ZStd(IAllocatorAllocate* workMemAllocator)
ZStd::ZStd(IAllocator* workMemAllocator)
{
m_workMemoryAllocator = workMemAllocator;
if (!m_workMemoryAllocator)
@@ -41,13 +41,13 @@ ZStd::~ZStd()
void* ZStd::AllocateMem(void* userData, size_t size)
{
IAllocatorAllocate* allocator = reinterpret_cast<IAllocatorAllocate*>(userData);
IAllocator* allocator = reinterpret_cast<IAllocator*>(userData);
return allocator->Allocate(size, 4, 0, "ZStandard", __FILE__, __LINE__);
}
void ZStd::FreeMem(void* userData, void* address)
{
IAllocatorAllocate* allocator = reinterpret_cast<IAllocatorAllocate*>(userData);
IAllocator* allocator = reinterpret_cast<IAllocator*>(userData);
allocator->DeAllocate(address);
}
@@ -17,12 +17,11 @@
namespace AZ
{
class IAllocator;
class IAllocatorAllocate;
class ZStd
{
public:
ZStd(IAllocatorAllocate* workMemAllocator = 0);
ZStd(IAllocator* workMemAllocator = 0);
~ZStd();
enum FlushType
@@ -77,7 +76,7 @@ namespace AZ
ZSTD_CStream* m_streamCompression;
ZSTD_DStream* m_streamDecompression;
IAllocatorAllocate* m_workMemoryAllocator;
IAllocator* m_workMemoryAllocator;
ZSTD_inBuffer m_inBuffer;
ZSTD_outBuffer m_outBuffer;
size_t m_nextBlockSize;
@@ -53,7 +53,6 @@ namespace AZ::Dom
ValueAllocator()
: Base("DomValueAllocator", "Allocator for AZ::Dom::Value")
{
DisableOverriding();
}
};
@@ -10,11 +10,6 @@
#include <AzCore/Debug/Budget.h>
#include <AzCore/Statistics/StatisticalProfilerProxy.h>
#ifdef USE_PIX
#include <AzCore/PlatformIncl.h>
#include <WinPixEventRuntime/pix3.h>
#endif
#if defined(AZ_PROFILER_MACRO_DISABLE) // by default we never disable the profiler registers as their overhead should be minimal, you can
// still do that for your code though.
#define AZ_PROFILE_SCOPE(...)
+31 -25
View File
@@ -10,44 +10,48 @@
namespace AZ::Debug
{
namespace Platform
{
template<typename... T>
void BeginProfileRegion(Budget* budget, const char* eventName, T const&... args);
void BeginProfileRegion(Budget* budget, const char* eventName);
void EndProfileRegion(Budget* budget);
} // namespace Platform
template<typename... T>
void ProfileScope::BeginRegion(
[[maybe_unused]] Budget* budget, [[maybe_unused]] const char* eventName, [[maybe_unused]] T const&... args)
{
if (!budget)
#if !defined(_RELEASE)
if (budget)
{
return;
}
#if !defined(_RELEASE)
// TODO: Verification that the supplied system name corresponds to a known budget
#if defined(USE_PIX)
PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...);
#endif
budget->BeginProfileRegion();
Platform::BeginProfileRegion(budget, eventName, args...);
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
{
profiler->BeginRegion(budget, eventName);
budget->BeginProfileRegion();
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
{
profiler->BeginRegion(budget, eventName);
}
}
#endif
#endif // #if !defined(_RELEASE)
}
inline void ProfileScope::EndRegion([[maybe_unused]] Budget* budget)
{
if (!budget)
#if !defined(_RELEASE)
if (budget)
{
return;
budget->EndProfileRegion();
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
{
profiler->EndRegion(budget);
}
Platform::EndProfileRegion(budget);
}
#if !defined(_RELEASE)
budget->EndProfileRegion();
#if defined(USE_PIX)
PIXEndEvent();
#endif
if (auto profiler = AZ::Interface<Profiler>::Get(); profiler)
{
profiler->EndRegion(budget);
}
#endif
#endif // !defined(_RELEASE)
}
template<typename... T>
@@ -63,3 +67,5 @@ namespace AZ::Debug
}
} // namespace AZ::Debug
#include <AzCore/Debug/Profiler_Platform.inl>
@@ -48,7 +48,7 @@ namespace AZ
public:
AZ_CLASS_ALLOCATOR(CompressorZStdData, AZ::SystemAllocator, 0);
CompressorZStdData(IAllocatorAllocate* zstdMemAllocator = 0)
CompressorZStdData(IAllocator* zstdMemAllocator = 0)
{
m_zstd = zstdMemAllocator;
}
@@ -21,7 +21,7 @@ namespace AZ::IO::IStreamerTypes
: m_allocator(AZ::AllocatorInstance<AZ::SystemAllocator>::Get())
{}
DefaultRequestMemoryAllocator::DefaultRequestMemoryAllocator(AZ::IAllocatorAllocate& allocator)
DefaultRequestMemoryAllocator::DefaultRequestMemoryAllocator(AZ::IAllocator& allocator)
: m_allocator(allocator)
{}
@@ -137,7 +137,7 @@ namespace AZ::IO::IStreamerTypes
public:
//! DefaultRequestMemoryAllocator wraps around the AZ::SystemAllocator by default.
DefaultRequestMemoryAllocator();
explicit DefaultRequestMemoryAllocator(AZ::IAllocatorAllocate& allocator);
explicit DefaultRequestMemoryAllocator(AZ::IAllocator& allocator);
~DefaultRequestMemoryAllocator() override;
void LockAllocator() override;
@@ -151,7 +151,7 @@ namespace AZ::IO::IStreamerTypes
private:
AZStd::atomic_int m_lockCounter{ 0 };
AZStd::atomic_int m_allocationCounter{ 0 };
AZ::IAllocatorAllocate& m_allocator;
AZ::IAllocator& m_allocator;
};
// The following alignment functions are put here until they're available in AzCore's math library.
+15 -15
View File
@@ -15,9 +15,9 @@ namespace AZ::IO
// Class template instantations
template class BasicPath<AZStd::string>;
template class BasicPath<FixedMaxPathString>;
template class PathIterator<PathView>;
template class PathIterator<Path>;
template class PathIterator<FixedMaxPath>;
template class PathIterator<const PathView>;
template class PathIterator<const Path>;
template class PathIterator<const FixedMaxPath>;
// Swap function instantiations
template void swap<AZStd::string>(Path& lhs, Path& rhs) noexcept;
@@ -38,16 +38,16 @@ namespace AZ::IO
const typename BasicPath<FixedMaxPathString>::value_type* rhs);
// Iterator compare instantiations
template bool operator==<PathView>(const PathIterator<PathView>& lhs,
const PathIterator<PathView>& rhs);
template bool operator==<Path>(const PathIterator<Path>& lhs,
const PathIterator<Path>& rhs);
template bool operator==<FixedMaxPath>(const PathIterator<FixedMaxPath>& lhs,
const PathIterator<FixedMaxPath>& rhs);
template bool operator!=<PathView>(const PathIterator<PathView>& lhs,
const PathIterator<PathView>& rhs);
template bool operator!=<Path>(const PathIterator<Path>& lhs,
const PathIterator<Path>& rhs);
template bool operator!=<FixedMaxPath>(const PathIterator<FixedMaxPath>& lhs,
const PathIterator<FixedMaxPath>& rhs);
template bool operator==<const PathView>(const PathIterator<const PathView>& lhs,
const PathIterator<const PathView>& rhs);
template bool operator==<const Path>(const PathIterator<const Path>& lhs,
const PathIterator<const Path>& rhs);
template bool operator==<const FixedMaxPath>(const PathIterator<const FixedMaxPath>& lhs,
const PathIterator<const FixedMaxPath>& rhs);
template bool operator!=<const PathView>(const PathIterator<const PathView>& lhs,
const PathIterator<const PathView>& rhs);
template bool operator!=<const Path>(const PathIterator<const Path>& lhs,
const PathIterator<const Path>& rhs);
template bool operator!=<const FixedMaxPath>(const PathIterator<const FixedMaxPath>& lhs,
const PathIterator<const FixedMaxPath>& rhs);
}
+11 -10
View File
@@ -43,9 +43,9 @@ namespace AZ::IO
public:
using string_view_type = AZStd::string_view;
using value_type = char;
using const_iterator = const PathIterator<PathView>;
using const_iterator = PathIterator<const PathView>;
using iterator = const_iterator;
friend PathIterator<PathView>;
friend const_iterator;
// constructors and destructor
constexpr PathView() = default;
@@ -319,9 +319,9 @@ namespace AZ::IO
using value_type = typename StringType::value_type;
using traits_type = typename StringType::traits_type;
using string_view_type = AZStd::string_view;
using const_iterator = const PathIterator<BasicPath>;
using const_iterator = PathIterator<const BasicPath>;
using iterator = const_iterator;
friend PathIterator<BasicPath>;
friend const_iterator;
// constructors and destructor
constexpr BasicPath() = default;
@@ -692,7 +692,7 @@ namespace AZ::IO
friend PathType;
using iterator_category = AZStd::bidirectional_iterator_tag;
using value_type = PathType;
using value_type = AZStd::remove_cv_t<PathType>;
using difference_type = ptrdiff_t;
using pointer = const value_type*;
using reference = const value_type&;
@@ -703,8 +703,9 @@ namespace AZ::IO
constexpr PathIterator() = default;
constexpr PathIterator(const PathIterator&) = default;
constexpr PathIterator(PathIterator&&) noexcept = default;
constexpr PathIterator& operator=(const PathIterator&) = default;
constexpr PathIterator& operator=(PathIterator&&) noexcept = default;
constexpr reference operator*() const;
@@ -733,10 +734,10 @@ namespace AZ::IO
ParserState m_state{ Singular };
};
template <typename PathType1>
constexpr bool operator==(const PathIterator<PathType1>& lhs, const PathIterator<PathType1>& rhs);
template <typename PathType1>
constexpr bool operator!=(const PathIterator<PathType1>& lhs, const PathIterator<PathType1>& rhs);
template <typename PathType>
constexpr bool operator==(const PathIterator<PathType>& lhs, const PathIterator<PathType>& rhs);
template <typename PathType>
constexpr bool operator!=(const PathIterator<PathType>& lhs, const PathIterator<PathType>& rhs);
}
#include <AzCore/IO/Path/Path.inl>
+16 -16
View File
@@ -399,7 +399,7 @@ namespace AZ::IO
constexpr auto PathView::begin() const -> const_iterator
{
auto pathParser = parser::PathParser::CreateBegin(m_path, m_preferred_separator);
PathIterator<PathView> it;
const_iterator it;
it.m_path_ref = this;
it.m_state = static_cast<typename const_iterator::ParserState>(pathParser.m_parser_state);
it.m_path_entry_view = pathParser.m_path_raw_entry;
@@ -409,7 +409,7 @@ namespace AZ::IO
constexpr auto PathView::end() const -> const_iterator
{
PathIterator<PathView> it;
const_iterator it;
it.m_state = const_iterator::AtEnd;
it.m_path_ref = this;
return it;
@@ -1262,7 +1262,7 @@ namespace AZ::IO
constexpr auto BasicPath<StringType>::begin() const -> const_iterator
{
auto pathParser = parser::PathParser::CreateBegin(m_path, m_preferred_separator);
PathIterator<BasicPath> it;
const_iterator it;
it.m_path_ref = this;
it.m_state = static_cast<typename const_iterator::ParserState>(pathParser.m_parser_state);
it.m_path_entry_view = pathParser.m_path_raw_entry;
@@ -1273,7 +1273,7 @@ namespace AZ::IO
template <typename StringType>
constexpr auto BasicPath<StringType>::end() const -> const_iterator
{
PathIterator<BasicPath> it;
const_iterator it;
it.m_state = const_iterator::AtEnd;
it.m_path_ref = this;
return it;
@@ -1529,16 +1529,16 @@ namespace AZ::IO
const typename BasicPath<FixedMaxPathString>::value_type* rhs);
// Iterator compare explicit declarations
extern template bool operator==<PathView>(const PathIterator<PathView>& lhs,
const PathIterator<PathView>& rhs);
extern template bool operator==<Path>(const PathIterator<Path>& lhs,
const PathIterator<Path>& rhs);
extern template bool operator==<FixedMaxPath>(const PathIterator<FixedMaxPath>& lhs,
const PathIterator<FixedMaxPath>& rhs);
extern template bool operator!=<PathView>(const PathIterator<PathView>& lhs,
const PathIterator<PathView>& rhs);
extern template bool operator!=<Path>(const PathIterator<Path>& lhs,
const PathIterator<Path>& rhs);
extern template bool operator!=<FixedMaxPath>(const PathIterator<FixedMaxPath>& lhs,
const PathIterator<FixedMaxPath>& rhs);
extern template bool operator==<const PathView>(const PathIterator<const PathView>& lhs,
const PathIterator<const PathView>& rhs);
extern template bool operator==<const Path>(const PathIterator<const Path>& lhs,
const PathIterator<const Path>& rhs);
extern template bool operator==<const FixedMaxPath>(const PathIterator<const FixedMaxPath>& lhs,
const PathIterator<const FixedMaxPath>& rhs);
extern template bool operator!=<const PathView>(const PathIterator<const PathView>& lhs,
const PathIterator<const PathView>& rhs);
extern template bool operator!=<const Path>(const PathIterator<const Path>& lhs,
const PathIterator<const Path>& rhs);
extern template bool operator!=<const FixedMaxPath>(const PathIterator<const FixedMaxPath>& lhs,
const PathIterator<const FixedMaxPath>& rhs);
}
@@ -10,6 +10,7 @@
#include <AzCore/AzCore_Traits_Platform.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/concepts/concepts.h>
namespace AZ::IO::Internal
{
@@ -17,7 +18,7 @@ namespace AZ::IO::Internal
{
return elem == '/' || elem == '\\';
}
template <typename InputIt, typename EndIt, typename = AZStd::enable_if_t<AZStd::Internal::is_input_iterator_v<InputIt>>>
template <typename InputIt, typename EndIt, typename = AZStd::enable_if_t<AZStd::input_iterator<InputIt>>>
static constexpr bool HasDrivePrefix(InputIt first, EndIt last)
{
size_t prefixSize = AZStd::distance(first, last);
@@ -46,7 +47,7 @@ namespace AZ::IO::Internal
//! Windows root names can have include drive letter within them
template <typename InputIt>
constexpr auto ConsumeRootName(InputIt entryBeginIter, InputIt entryEndIter, const char preferredSeparator)
-> AZStd::enable_if_t<AZStd::Internal::is_forward_iterator_v<InputIt>, InputIt>
-> AZStd::enable_if_t<AZStd::forward_iterator<InputIt>, InputIt>
{
if (preferredSeparator == PosixPathSeparator)
{
@@ -147,7 +148,7 @@ namespace AZ::IO::Internal
//! If the preferred separator is '/' just checks if the path starts with a '/
//! Otherwise a check for a Windows absolute path occurs
//! Windows absolute paths can include a RootName
template <typename InputIt, typename EndIt, typename = AZStd::enable_if_t<AZStd::Internal::is_input_iterator_v<InputIt>>>
template <typename InputIt, typename EndIt, typename = AZStd::enable_if_t<AZStd::input_iterator<InputIt>>>
static constexpr bool IsAbsolute(InputIt first, EndIt last, const char preferredSeparator)
{
size_t pathSize = AZStd::distance(first, last);
@@ -208,11 +209,11 @@ namespace AZ::IO::parser
enum ParserState : uint8_t
{
// Zero is a special sentinel value used by default constructed iterators.
PS_BeforeBegin = PathIterator<PathView>::BeforeBegin,
PS_InRootName = PathIterator<PathView>::InRootName,
PS_InRootDir = PathIterator<PathView>::InRootDir,
PS_InFilenames = PathIterator<PathView>::InFilenames,
PS_AtEnd = PathIterator<PathView>::AtEnd
PS_BeforeBegin = PathView::const_iterator::BeforeBegin,
PS_InRootName = PathView::const_iterator::InRootName,
PS_InRootDir = PathView::const_iterator::InRootDir,
PS_InFilenames = PathView::const_iterator::InFilenames,
PS_AtEnd = PathView::const_iterator::AtEnd
};
struct PathParser
@@ -1,197 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_JOBS_JOBEXECUTOR_H
#define AZCORE_JOBS_JOBEXECUTOR_H
#pragma once
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/std/parallel/condition_variable.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ
{
/**
* Helper for porting legacy jobs that allows Starting and Waiting for multiple jobs asynchronously
*/
class LegacyJobExecutor final
{
public:
LegacyJobExecutor() = default;
LegacyJobExecutor(const LegacyJobExecutor&) = delete;
~LegacyJobExecutor()
{
WaitForCompletion();
}
template <class Function>
inline void StartJob(const Function& processFunction, JobContext* context = nullptr)
{
Job * job = aznew JobFunctionExecutorHelper<Function>(processFunction, *this, context);
StartJobInternal(job);
}
// SetPostJob - This API exists to support backwards compatibility and is not a recommended pattern to be copied.
// Instead, create AZ::Jobs with appropriate dependencies on each other
template <class Function>
inline void SetPostJob(LegacyJobExecutor& postJobExecutor, const Function& processFunction, JobContext* context = nullptr)
{
AZStd::unique_ptr<JobExecutorHelper> postJob(aznew JobFunctionExecutorHelper<Function>(processFunction, postJobExecutor, context)); // Allocate outside the lock
{
LockGuard lockGuard(m_conditionLock);
AZ_Assert(!m_postJob, "Post already set");
AZ_Assert(!m_running, "LegacyJobExecutor::SetPostJob() must be called before starting any jobs");
m_postJob = std::move(postJob);
// Note: m_jobCount is not incremented until we push the post job
}
}
inline void ClearPostJob()
{
LockGuard lockGuard(m_conditionLock);
m_postJob.reset();
}
inline void Reset()
{
AZ_Assert(!IsRunning(), "LegacyJobExecutor::Reset() called while jobs in flight");
}
inline void WaitForCompletion()
{
AZStd::unique_lock<decltype(m_conditionLock)> uniqueLock(m_conditionLock);
while (m_running)
{
AZ_PROFILE_FUNCTION(AzCore);
m_completionCondition.wait(uniqueLock, [this] { return !this->m_running; });
}
}
// Push a logical fence that will cause WaitForCompletion to wait until PopCompletionFence is called and all jobs are complete. Analogue to the legacy API SJobState::SetStarted()
// Note: this does NOT fence execution of jobs in relation to each other
inline void PushCompletionFence()
{
IncJobCount();
}
// Pop a logical completion fence. Analogue to the legacy API SJobState::SetStopped()
inline void PopCompletionFence()
{
JobCompleteUpdate();
}
// Are there presently jobs in-flight (queued or running)?
inline bool IsRunning()
{
return m_running;
}
private:
void JobCompleteUpdate()
{
AZ_Assert(m_jobCount, "Invalid LegacyJobExecutor::m_jobCount.");
if (--m_jobCount == 0) // note: m_jobCount is atomic, so only the last completing job will take the count to zero
{
JobExecutorHelper* postJob = nullptr;
{
// All state transitions to and from running must be serialized through the condition lock
LockGuard lockGuard(m_conditionLock);
// Test count again as another job may have started before we got the lock
if (!m_jobCount)
{
m_running = false;
postJob = m_postJob.release();
m_completionCondition.notify_all();
}
}
// outside the lock (this pointer is no longer valid)...
if (postJob)
{
postJob->StartOnExecutor();
}
}
}
void StartJobInternal(Job * job)
{
IncJobCount();
job->Start();
}
void IncJobCount()
{
if (m_jobCount++ == 0)
{
// All state transitions to and from running must be serialized through the condition lock (Even though m_running is atomic)
LockGuard lockGuard(m_conditionLock);
m_running = true;
}
}
class JobExecutorHelper
{
public:
virtual ~JobExecutorHelper() = default;
virtual void StartOnExecutor() = 0;
};
/**
* Private Job type that notifies the owning LegacyJobExecutor of completion
*/
template<class Function>
class JobFunctionExecutorHelper : public JobFunction<Function>, public JobExecutorHelper
{
using Base = JobFunction<Function>;
public:
AZ_CLASS_ALLOCATOR(JobFunctionExecutorHelper, ThreadPoolAllocator, 0)
JobFunctionExecutorHelper(typename JobFunction<Function>::FunctionCRef processFunction, LegacyJobExecutor& executor, JobContext* context)
: JobFunction<Function>(processFunction, true /* isAutoDelete */, context)
, m_executor(executor)
{
}
void StartOnExecutor() override
{
m_executor.StartJobInternal(this);
}
void Process() override
{
Base::Process();
m_executor.JobCompleteUpdate();
}
private:
LegacyJobExecutor& m_executor;
};
template<class Function>
friend class JobFunctionExecutorHelper; // For JobCompleteUpdate, StartJobInternal
using Lock = AZStd::mutex;
using LockGuard = AZStd::lock_guard<Lock>;
AZStd::condition_variable m_completionCondition;
Lock m_conditionLock;
AZStd::unique_ptr<JobExecutorHelper> m_postJob;
AZStd::atomic_uint m_jobCount{0};
AZStd::atomic_bool m_running{false};
};
}
#endif
@@ -9,11 +9,12 @@
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/AllocatorManager.h>
#define RECORDING_ENABLED 0
// Only used to create recordings of memory operations to use for memory benchmarks
#define O3DE_RECORDING_ENABLED 0
#if RECORDING_ENABLED
#if O3DE_RECORDING_ENABLED
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/scoped_lock.h>
@@ -62,23 +63,24 @@ namespace
static constexpr size_t s_maxNumberOfAllocationsToRecord = 16384;
static size_t s_numberOfAllocationsRecorded = 0;
static constexpr size_t s_allocationOperationCount = 5 * 1024;
static constexpr size_t s_allocationOperationCount = 8 * 1024;
static AZStd::array<AllocatorOperation, s_allocationOperationCount> s_operations = {};
static uint64_t s_operationCounter = 0;
static unsigned int s_nextRecordId = 1;
using AllocatorOperationByAddress = AZStd::unordered_map<void*, AllocatorOperation, AZStd::less<void*>, DebugAllocator>;
using AllocatorOperationByAddress = AZStd::map<void*, AllocatorOperation, AZStd::less<void*>, DebugAllocator>;
static AllocatorOperationByAddress s_allocatorOperationByAddress;
using AvailableRecordIds = AZStd::vector<unsigned int, DebugAllocator>;
AvailableRecordIds s_availableRecordIds;
void RecordAllocatorOperation(AllocatorOperation::OperationType type, void* ptr, size_t size = 0, size_t alignment = 0)
{
AZStd::scoped_lock<AZStd::mutex> lock(s_operationsMutex);
AZStd::scoped_lock lock(s_operationsMutex);
if (s_operationCounter == s_allocationOperationCount)
{
AZ::IO::SystemFile file;
int mode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND | AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
// memoryrecordings.bin is being output to the current working directory
if (!file.Exists("memoryrecordings.bin"))
{
mode |= AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE;
@@ -158,8 +160,8 @@ namespace
namespace AZ
{
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc)
: IAllocator(allocationSource)
AllocatorBase::AllocatorBase(IAllocatorSchema* allocationSchema, const char* name, const char* desc)
: IAllocator(allocationSchema)
, m_name(name)
, m_desc(desc)
{
@@ -184,11 +186,6 @@ namespace AZ
return m_desc;
}
IAllocatorAllocate* AllocatorBase::GetSchema()
{
return nullptr;
}
Debug::AllocationRecords* AllocatorBase::GetRecords()
{
return m_records;
@@ -205,11 +202,6 @@ namespace AZ
return m_isReady;
}
bool AllocatorBase::CanBeOverridden() const
{
return m_canBeOverridden;
}
void AllocatorBase::PostCreate()
{
if (m_registrationEnabled)
@@ -272,11 +264,6 @@ namespace AZ
return m_isProfilingActive;
}
void AllocatorBase::DisableOverriding()
{
m_canBeOverridden = false;
}
void AllocatorBase::DisableRegistration()
{
m_registrationEnabled = false;
@@ -285,10 +272,6 @@ namespace AZ
void AllocatorBase::ProfileAllocation(
void* ptr, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, int suppressStackRecord)
{
#if defined(AZ_HAS_VARIADIC_TEMPLATES) && defined(AZ_DEBUG_BUILD)
++suppressStackRecord; // one more for the fact the ebus is a function
#endif // AZ_HAS_VARIADIC_TEMPLATES
if (m_isProfilingActive)
{
auto records = GetRecords();
@@ -298,7 +281,7 @@ namespace AZ
}
}
#if RECORDING_ENABLED
#if O3DE_RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, byteSize, alignment);
#endif
}
@@ -313,7 +296,7 @@ namespace AZ
records->UnregisterAllocation(ptr, byteSize, alignment, info);
}
}
#if RECORDING_ENABLED
#if O3DE_RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr, byteSize, alignment);
#endif
}
@@ -330,7 +313,7 @@ namespace AZ
ProfileDeallocation(ptr, 0, 0, &info);
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
}
#if RECORDING_ENABLED
#if O3DE_RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr);
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, newPtr, newSize, newAlignment);
#endif
@@ -351,7 +334,7 @@ namespace AZ
records->ResizeAllocation(ptr, newSize);
}
}
#if RECORDING_ENABLED
#if O3DE_RECORDING_ENABLED
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, newSize);
#endif
}
@@ -22,7 +22,7 @@ namespace AZ
class AllocatorBase : public IAllocator
{
protected:
AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc);
AllocatorBase(IAllocatorSchema* allocationSchema, const char* name, const char* desc);
~AllocatorBase();
public:
@@ -32,11 +32,9 @@ namespace AZ
//---------------------------------------------------------------------
const char* GetName() const override;
const char* GetDescription() const override;
IAllocatorAllocate* GetSchema() override;
Debug::AllocationRecords* GetRecords() final;
void SetRecords(Debug::AllocationRecords* records) final;
bool IsReady() const final;
bool CanBeOverridden() const final;
void PostCreate() override;
void PreDestroy() final;
void SetLazilyCreated(bool lazy) final;
@@ -68,10 +66,6 @@ namespace AZ
return byteSize;
}
/// Call to disallow this allocator from being overridden.
/// Only kernel-level allocators where it would be especially problematic for them to be overridden should do this.
void DisableOverriding();
/// Call to disallow this allocator from being registered with the AllocatorManager.
/// Only kernel-level allocators where it would be especially problematic for them to be registered with the AllocatorManager should do this.
void DisableRegistration();
@@ -107,7 +101,6 @@ namespace AZ
bool m_isLazilyCreated = false;
bool m_isProfilingActive = false;
bool m_isReady = false;
bool m_canBeOverridden = true;
bool m_registrationEnabled = true;
};
@@ -12,17 +12,12 @@
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Memory/AllocatorOverrideShim.h>
#include <AzCore/Memory/MallocSchema.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/containers/array.h>
#if !defined(RELEASE) && !defined(AZCORE_MEMORY_ENABLE_OVERRIDES)
# define AZCORE_MEMORY_ENABLE_OVERRIDES
#endif
namespace AZ::Internal
{
struct AMStringHasher
@@ -54,18 +49,6 @@ namespace AZ::Internal
namespace AZ
{
struct AllocatorManager::InternalData
{
explicit InternalData(const AZStdIAllocator& alloc)
: m_allocatorMap(alloc)
, m_remappings(alloc)
, m_remappingsReverse(alloc)
{}
Internal::AllocatorNameMap m_allocatorMap;
Internal::AllocatorRemappings m_remappings;
Internal::AllocatorRemappings m_remappingsReverse;
};
static EnvironmentVariable<AllocatorManager> s_allocManager = nullptr;
static AllocatorManager* s_allocManagerDebug = nullptr; // For easier viewing in crash dumps
@@ -81,16 +64,6 @@ static Internal::PreEnvironmentAttachData& GetPreEnvironmentAttachData()
void AllocatorManager::PreRegisterAllocator(IAllocator* allocator)
{
auto& data = GetPreEnvironmentAttachData();
#ifdef AZCORE_MEMORY_ENABLE_OVERRIDES
// All allocators must switch to an OverrideEnabledAllocationSource proxy if they are to support allocator overriding.
if (allocator->CanBeOverridden())
{
auto shim = Internal::AllocatorOverrideShim::Create(allocator, &data.m_mallocSchema);
allocator->SetAllocationSource(shim);
}
#endif
{
AZStd::lock_guard<AZStd::mutex> lock(data.m_mutex);
AZ_Assert(data.m_unregisteredAllocatorCount < Internal::PreEnvironmentAttachData::MAX_UNREGISTERED_ALLOCATORS, "Too many allocators trying to register before environment attached!");
@@ -175,12 +148,9 @@ AllocatorManager::AllocatorManager()
}
)
{
m_overrideSource = nullptr;
m_numAllocators = 0;
m_isAllocatorLeaking = false;
m_configurationFinalized = false;
m_defaultTrackingRecordMode = Debug::AllocationRecords::RECORD_NO_RECORDS;
m_data = new (m_mallocSchema->Allocate(sizeof(InternalData), AZStd::alignment_of<InternalData>::value, 0)) InternalData(AZStdIAllocator(m_mallocSchema.get()));
}
//=========================================================================
@@ -210,10 +180,6 @@ AllocatorManager::RegisterAllocator(class IAllocator* alloc)
alloc->SetProfilingActive(m_profilingRefcount.load() > 0);
m_allocators[m_numAllocators++] = alloc;
#ifdef AZCORE_MEMORY_ENABLE_OVERRIDES
ConfigureAllocatorOverrides(alloc);
#endif
}
//=========================================================================
@@ -232,81 +198,12 @@ AllocatorManager::InternalDestroy()
// Do not actually destroy the lazy allocator as it may have work to do during non-deterministic shutdown
}
if (m_data)
{
m_data->~InternalData();
m_mallocSchema->DeAllocate(m_data);
m_data = nullptr;
}
if (!m_isAllocatorLeaking)
{
AZ_Assert(m_numAllocators == 0, "There are still %d registered allocators!", m_numAllocators);
}
}
//=========================================================================
// ConfigureAllocatorOverrides
// [10/14/2018]
//=========================================================================
void
AllocatorManager::ConfigureAllocatorOverrides(IAllocator* alloc)
{
auto record = m_data->m_allocatorMap.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(alloc->GetName(), AZStdIAllocator(m_mallocSchema.get())), AZStd::forward_as_tuple(alloc));
// We only need to keep going if the allocator supports overrides.
if (!alloc->CanBeOverridden())
{
return;
}
if (!alloc->IsAllocationSourceChanged())
{
// All allocators must switch to an OverrideEnabledAllocationSource proxy if they are to support allocator overriding.
auto overrideEnabled = Internal::AllocatorOverrideShim::Create(alloc, m_mallocSchema.get());
alloc->SetAllocationSource(overrideEnabled);
}
auto itr = m_data->m_remappings.find(record.first->first);
if (itr != m_data->m_remappings.end())
{
auto remapTo = m_data->m_allocatorMap.find(itr->second);
if (remapTo != m_data->m_allocatorMap.end())
{
static_cast<Internal::AllocatorOverrideShim*>(alloc->GetAllocationSource())->SetOverride(remapTo->second->GetOriginalAllocationSource());
}
}
itr = m_data->m_remappingsReverse.find(record.first->first);
if (itr != m_data->m_remappingsReverse.end())
{
auto remapFrom = m_data->m_allocatorMap.find(itr->second);
if (remapFrom != m_data->m_allocatorMap.end())
{
AZ_Assert(!m_configurationFinalized, "Allocators may only remap to allocators that have been created before configuration finalization");
static_cast<Internal::AllocatorOverrideShim*>(remapFrom->second->GetAllocationSource())->SetOverride(alloc->GetOriginalAllocationSource());
}
}
if (m_overrideSource)
{
static_cast<Internal::AllocatorOverrideShim*>(alloc->GetAllocationSource())->SetOverride(m_overrideSource);
}
if (m_configurationFinalized)
{
// We can get rid of the intermediary if configuration won't be changing any further.
// (The creation of it at the top of this function was superflous, but it made it easier to set things up going through a single code path.)
auto shim = static_cast<Internal::AllocatorOverrideShim*>(alloc->GetAllocationSource());
alloc->SetAllocationSource(shim->GetOverride());
Internal::AllocatorOverrideShim::Destroy(shim);
}
}
//=========================================================================
// UnRegisterAllocator
// [9/17/2009]
@@ -365,7 +262,7 @@ AllocatorManager::GarbageCollect()
for (int i = 0; i < m_numAllocators; ++i)
{
m_allocators[i]->GetAllocationSource()->GarbageCollect();
m_allocators[i]->GetSchema()->GarbageCollect();
}
}
@@ -414,94 +311,6 @@ AllocatorManager::SetTrackingMode(Debug::AllocationRecords::Mode mode)
}
}
//=========================================================================
// SetOverrideSchema
// [8/17/2018]
//=========================================================================
void
AllocatorManager::SetOverrideAllocatorSource(IAllocatorAllocate* source, bool overrideExistingAllocators)
{
(void)source;
(void)overrideExistingAllocators;
#ifdef AZCORE_MEMORY_ENABLE_OVERRIDES
AZ_Assert(!m_configurationFinalized, "You cannot set an allocator source after FinalizeConfiguration() has been called.");
m_overrideSource = source;
if (overrideExistingAllocators)
{
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
for (int i = 0; i < m_numAllocators; ++i)
{
if (m_allocators[i]->CanBeOverridden())
{
auto shim = static_cast<Internal::AllocatorOverrideShim*>(m_allocators[i]->GetAllocationSource());
shim->SetOverride(source);
}
}
}
#endif
}
//=========================================================================
// AddAllocatorRemapping
// [8/27/2018]
//=========================================================================
void
AllocatorManager::AddAllocatorRemapping(const char* fromName, const char* toName)
{
(void)fromName;
(void)toName;
#ifdef AZCORE_MEMORY_ENABLE_OVERRIDES
AZ_Assert(!m_configurationFinalized, "You cannot set an allocator remapping after FinalizeConfiguration() has been called.");
m_data->m_remappings.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(fromName, m_mallocSchema.get()), AZStd::forward_as_tuple(toName, m_mallocSchema.get()));
m_data->m_remappingsReverse.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(toName, m_mallocSchema.get()), AZStd::forward_as_tuple(fromName, m_mallocSchema.get()));
#endif
}
void
AllocatorManager::FinalizeConfiguration()
{
if (m_configurationFinalized)
{
return;
}
#ifdef AZCORE_MEMORY_ENABLE_OVERRIDES
{
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
for (int i = 0; i < m_numAllocators; ++i)
{
if (!m_allocators[i]->CanBeOverridden())
{
continue;
}
auto shim = static_cast<Internal::AllocatorOverrideShim*>(m_allocators[i]->GetAllocationSource());
if (!shim->IsOverridden())
{
m_allocators[i]->ResetAllocationSource();
Internal::AllocatorOverrideShim::Destroy(shim);
}
else if (!shim->HasOrphanedAllocations())
{
m_allocators[i]->SetAllocationSource(shim->GetOverride());
Internal::AllocatorOverrideShim::Destroy(shim);
}
else
{
shim->SetFinalizedConfiguration();
}
}
}
#endif
m_configurationFinalized = true;
}
void
AllocatorManager::EnterProfilingMode()
{
@@ -545,27 +354,18 @@ AllocatorManager::DumpAllocators()
size_t totalConsumedBytes = 0;
memset(m_dumpInfo, 0, sizeof(m_dumpInfo));
void* sourceList[m_maxNumAllocators];
AZ_Printf(TAG, "%d allocators active\n", m_numAllocators);
AZ_Printf(TAG, "Index,Name,Used kb,Reserved kb,Consumed kb\n");
for (int i = 0; i < m_numAllocators; i++)
{
auto allocator = m_allocators[i];
auto source = allocator->GetAllocationSource();
IAllocator* allocator = GetAllocator(i);
const char* name = allocator->GetName();
size_t usedBytes = source->NumAllocatedBytes();
size_t reservedBytes = source->Capacity();
size_t usedBytes = allocator->NumAllocatedBytes();
size_t reservedBytes = allocator->Capacity();
size_t consumedBytes = reservedBytes;
// Very hacky and inefficient check to see if this allocator obtains its memory from another allocator
sourceList[i] = source;
if (AZStd::find(sourceList, sourceList + i, allocator->GetSchema()) != sourceList + i)
{
consumedBytes = 0;
}
totalUsedBytes += usedBytes;
totalReservedBytes += reservedBytes;
totalConsumedBytes += consumedBytes;
@@ -585,61 +385,21 @@ void AllocatorManager::GetAllocatorStats(size_t& allocatedBytes, size_t& capacit
AZStd::lock_guard<AZStd::mutex> lock(m_allocatorListMutex);
const int allocatorCount = GetNumAllocators();
AZStd::unordered_map<IAllocatorAllocate*, IAllocator*> existingAllocators;
AZStd::unordered_map<IAllocatorAllocate*, IAllocator*> sourcesToAllocators;
// Build a mapping of original allocator sources to their allocators
for (int i = 0; i < allocatorCount; ++i)
{
IAllocator* allocator = GetAllocator(i);
sourcesToAllocators.emplace(allocator->GetOriginalAllocationSource(), allocator);
}
for (int i = 0; i < allocatorCount; ++i)
{
IAllocator* allocator = GetAllocator(i);
IAllocatorAllocate* source = allocator->GetAllocationSource();
IAllocatorAllocate* originalSource = allocator->GetOriginalAllocationSource();
IAllocatorAllocate* schema = allocator->GetSchema();
IAllocator* alias = (source != originalSource) ? sourcesToAllocators[source] : nullptr;
if (schema && !alias)
{
// Check to see if this allocator's source maps to another allocator
// Need to check both the schema and the allocator itself, as either one might be used as the alias depending on how it's implemented
AZStd::array<IAllocatorAllocate*, 2> checkAllocators = { { schema, allocator->GetAllocationSource() } };
for (IAllocatorAllocate* check : checkAllocators)
{
auto existing = existingAllocators.emplace(check, allocator);
if (!existing.second)
{
alias = existing.first->second;
// Do not break out of the loop as we need to add to the map for all entries
}
}
}
static const IAllocator* OS_ALLOCATOR = &AllocatorInstance<OSAllocator>::GetAllocator();
size_t sourceAllocatedBytes = source->NumAllocatedBytes();
size_t sourceCapacityBytes = source->Capacity();
if (allocator == OS_ALLOCATOR)
{
// Need to special case the OS allocator because its capacity is a made-up number. Better to just use the allocated amount, it will hopefully be small anyway.
sourceCapacityBytes = sourceAllocatedBytes;
}
allocatedBytes += allocator->NumAllocatedBytes();
capacityBytes += allocator->Capacity();
if (outStats)
{
outStats->emplace(outStats->end(), allocator->GetName(), alias ? alias->GetName() : allocator->GetDescription(), sourceAllocatedBytes, sourceCapacityBytes, alias != nullptr);
}
if (!alias)
{
allocatedBytes += sourceAllocatedBytes;
capacityBytes += sourceCapacityBytes;
outStats->emplace(outStats->end(),
allocator->GetName(),
allocator->GetDescription(),
allocator->NumAllocatedBytes(),
allocator->Capacity());
}
}
}
@@ -87,17 +87,6 @@ namespace AZ
/// Especially for great code and engines...
void SetAllocatorLeaking(bool allowLeaking) { m_isAllocatorLeaking = allowLeaking; }
/// Set an override allocator
/// All allocators registered with the AllocatorManager will automatically redirect to this allocator
/// if set.
void SetOverrideAllocatorSource(IAllocatorAllocate* source, bool overrideExistingAllocators = true);
/// Retrieve the override schema
IAllocatorAllocate* GetOverrideAllocatorSource() const { return m_overrideSource; }
void AddAllocatorRemapping(const char* fromName, const char* toName);
void FinalizeConfiguration();
/// Enter or exit profiling mode; calls to Enter must be matched with calls to Exit
void EnterProfilingMode();
void ExitProfilingMode();
@@ -116,19 +105,17 @@ namespace AZ
struct AllocatorStats
{
AllocatorStats(const char* name, const char* aliasOrDescription, size_t allocatedBytes, size_t capacityBytes, bool isAlias)
AllocatorStats(const char* name, const char* aliasOrDescription, size_t allocatedBytes, size_t capacityBytes)
: m_name(name)
, m_aliasOrDescription(aliasOrDescription)
, m_allocatedBytes(allocatedBytes)
, m_capacityBytes(capacityBytes)
, m_isAlias(isAlias)
{}
AZStd::string m_name;
AZStd::string m_aliasOrDescription;
size_t m_allocatedBytes;
size_t m_capacityBytes;
bool m_isAlias;
};
void GetAllocatorStats(size_t& usedBytes, size_t& reservedBytes, AZStd::vector<AllocatorStats>* outStats = nullptr);
@@ -160,7 +147,6 @@ namespace AZ
private:
void InternalDestroy();
void ConfigureAllocatorOverrides(IAllocator* alloc);
void DebugBreak(void* address, const Debug::AllocationInfo& info);
AZ::MallocSchema* CreateMallocSchema();
@@ -175,14 +161,9 @@ namespace AZ
MemoryBreak m_memoryBreak[MaxNumMemoryBreaks];
char m_activeBreaks;
AZStd::mutex m_allocatorListMutex;
IAllocatorAllocate* m_overrideSource;
DumpInfo m_dumpInfo[m_maxNumAllocators];
struct InternalData;
InternalData* m_data;
bool m_configurationFinalized;
AZStd::atomic<int> m_profilingRefcount;
AZ::Debug::AllocationRecords::Mode m_defaultTrackingRecordMode;
@@ -1,227 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Memory/AllocatorOverrideShim.h>
namespace AZ::Internal
{
AllocatorOverrideShim* AllocatorOverrideShim::Create(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource)
{
void* memory = shimAllocationSource->Allocate(sizeof(AllocatorOverrideShim), AZStd::alignment_of<AllocatorOverrideShim>::value, 0);
auto result = new (memory) AllocatorOverrideShim(owningAllocator, shimAllocationSource);
return result;
}
void AllocatorOverrideShim::Destroy(AllocatorOverrideShim* source)
{
auto shimAllocationSource = source->m_shimAllocationSource;
source->~AllocatorOverrideShim();
shimAllocationSource->DeAllocate(source);
}
AllocatorOverrideShim::AllocatorOverrideShim(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource)
: m_owningAllocator(owningAllocator)
, m_source(owningAllocator->GetOriginalAllocationSource())
, m_overridingSource(owningAllocator->GetOriginalAllocationSource())
, m_shimAllocationSource(shimAllocationSource)
, m_records(typename AllocationSet::hasher(), typename AllocationSet::key_eq(), StdAllocationSrc(shimAllocationSource))
{
}
void AllocatorOverrideShim::SetOverride(IAllocatorAllocate* source)
{
m_overridingSource = source;
}
IAllocatorAllocate* AllocatorOverrideShim::GetOverride() const
{
return m_overridingSource;
}
bool AllocatorOverrideShim::IsOverridden() const
{
return m_source != m_overridingSource;
}
bool AllocatorOverrideShim::HasOrphanedAllocations() const
{
return !m_records.empty();
}
void AllocatorOverrideShim::SetFinalizedConfiguration()
{
m_finalizedConfiguration = true;
}
typename AllocatorOverrideShim::pointer_type AllocatorOverrideShim::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
{
pointer_type ptr = m_overridingSource->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord);
if (!IsOverridden())
{
lock_type lock(m_mutex);
m_records.insert(ptr); // Record in case we need to orphan this allocation later
}
return ptr;
}
void AllocatorOverrideShim::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
{
IAllocatorAllocate* source = m_overridingSource;
bool destroy = false;
{
lock_type lock(m_mutex);
// Check to see if this came from a prior allocation source
if (m_records.erase(ptr) && IsOverridden())
{
source = m_source;
if (m_records.empty() && m_finalizedConfiguration)
{
// All orphaned records are gone; we are no longer needed
m_owningAllocator->SetAllocationSource(m_overridingSource);
destroy = true; // Must destroy outside the lock
}
}
}
source->DeAllocate(ptr, byteSize, alignment);
if (destroy)
{
Destroy(this);
}
}
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::Resize(pointer_type ptr, size_type newSize)
{
IAllocatorAllocate* source = m_overridingSource;
if (IsOverridden())
{
// Determine who owns the allocation
lock_type lock(m_mutex);
if (m_records.count(ptr))
{
source = m_source;
}
}
size_t result = source->Resize(ptr, newSize);
return result;
}
typename AllocatorOverrideShim::pointer_type AllocatorOverrideShim::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
{
pointer_type newPtr = nullptr;
bool useOverride = true;
bool destroy = false;
if (IsOverridden())
{
lock_type lock(m_mutex);
if (m_records.erase(ptr))
{
// An old allocation needs to be transferred to the new, overriding allocator.
useOverride = false; // We'll do the reallocation here
size_t oldSize = m_source->AllocationSize(ptr);
if (newSize)
{
newPtr = m_overridingSource->Allocate(newSize, newAlignment, 0);
memcpy(newPtr, ptr, AZStd::min(newSize, oldSize));
}
m_source->DeAllocate(ptr, oldSize);
if (m_records.empty() && m_finalizedConfiguration)
{
// All orphaned records are gone; we are no longer needed
m_owningAllocator->SetAllocationSource(m_overridingSource);
destroy = true; // Must destroy outside the lock
}
}
}
if (useOverride)
{
// Default behavior, we weren't deleting an old allocation
newPtr = m_overridingSource->ReAllocate(ptr, newSize, newAlignment);
if (!IsOverridden())
{
// Still need to do bookkeeping if we haven't been overridden yet
lock_type lock(m_mutex);
m_records.erase(ptr);
m_records.insert(newPtr);
}
}
if (destroy)
{
Destroy(this);
}
return newPtr;
}
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::AllocationSize(pointer_type ptr)
{
IAllocatorAllocate* source = m_overridingSource;
if (IsOverridden())
{
// Determine who owns the allocation
lock_type lock(m_mutex);
if (m_records.count(ptr))
{
source = m_source;
}
}
return source->AllocationSize(ptr);
}
void AllocatorOverrideShim::GarbageCollect()
{
m_source->GarbageCollect();
}
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::NumAllocatedBytes() const
{
return m_source->NumAllocatedBytes();
}
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::Capacity() const
{
return m_source->Capacity();
}
typename AllocatorOverrideShim::size_type AllocatorOverrideShim::GetMaxAllocationSize() const
{
return m_source->GetMaxAllocationSize();
}
auto AllocatorOverrideShim::GetMaxContiguousAllocationSize() const -> size_type
{
return m_source->GetMaxContiguousAllocationSize();
}
IAllocatorAllocate* AllocatorOverrideShim::GetSubAllocator()
{
return m_source->GetSubAllocator();
}
} // namespace AZ::Internal
@@ -1,102 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/parallel/mutex.h>
namespace AZ
{
class AllocatorManager;
namespace Internal
{
/**
* A shim schema that solves the problem of overriding lazily-created allocators especially, that perform allocations before the override happens.
*
* Any allocator that *might* be overridden at some point must have this shim installed as its allocation source. This is done automatically by
* the AllocationManager; you generally do not have to interact with this shim directly at all.
*
* The shim will keep track of any allocations that occur, and if the allocator gets overridden it will ensure that prior allocations are
* deallocated with the old schema rather than the new one.
*
* There is some performance cost to this intrusion, however, in most cases it's only temporary:
* * Once the application calls FinalizeConfiguration(), any non-overridden allocators will have their shims destroyed.
* * Any overridden allocators that do not have prior allocations will have their shims destroyed.
* * Any overridden allocator will automatically destroy its shim once the last of the prior allocations has been deallocated.
*
* Note that an allocator that gets overridden but has prior allocations that it never intends to deallocate (such as file-level statics that never
* get changed) will keep its shim indefinitely. This is an unfortunate cost but only affects those allocators if they are being overridden.
*/
class AllocatorOverrideShim
: public IAllocatorAllocate
{
friend AllocatorManager;
public:
//---------------------------------------------------------------------
// IAllocator implementation
//---------------------------------------------------------------------
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = nullptr, const char* fileName = nullptr, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
size_type Resize(pointer_type ptr, size_type newSize) override;
pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override;
size_type AllocationSize(pointer_type ptr) override;
void GarbageCollect() override;
size_type NumAllocatedBytes() const override;
size_type Capacity() const override;
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
IAllocatorAllocate* GetSubAllocator() override;
private:
/// Creates a shim using a custom memory source
static AllocatorOverrideShim* Create(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource);
static void Destroy(AllocatorOverrideShim* source);
AllocatorOverrideShim(IAllocator* owningAllocator, IAllocatorAllocate* shimAllocationSource);
/// Overrides the shim's memory source with a different memory source.
void SetOverride(IAllocatorAllocate* source);
/// Returns the override source.
IAllocatorAllocate* GetOverride() const;
/// Returns true if the shim has an override source set on it.
bool IsOverridden() const;
/// Returns true if there are orphaned allocations from before the shim had its override set.
bool HasOrphanedAllocations() const;
/// Called by the AllocatorManager to signify that the configuration has been finalized by the application.
void SetFinalizedConfiguration();
private:
class StdAllocationSrc : public AZStdIAllocator
{
public:
StdAllocationSrc(IAllocatorAllocate* schema = nullptr) : AZStdIAllocator(schema)
{
}
};
typedef AZStd::mutex mutex_type;
typedef AZStd::lock_guard<mutex_type> lock_type;
typedef AZStd::unordered_set<void*, AZStd::hash<void*>, AZStd::equal_to<void*>, StdAllocationSrc> AllocationSet;
IAllocator* m_owningAllocator;
IAllocatorAllocate* m_source;
IAllocatorAllocate* m_overridingSource;
IAllocatorAllocate* m_shimAllocationSource;
AllocationSet m_records;
mutex_type m_mutex;
bool m_finalizedConfiguration = false;
};
}
}
@@ -20,8 +20,7 @@ namespace AZ
// [1/28/2011]
//=========================================================================
BestFitExternalMapAllocator::BestFitExternalMapAllocator()
: AllocatorBase(this, "BestFitExternalMapAllocator", "Best fit allocator with external tracking storage!")
, m_schema(nullptr)
: AllocatorBase(nullptr, "BestFitExternalMapAllocator", "Best fit allocator with external tracking storage!")
{
}
@@ -182,13 +181,4 @@ namespace AZ
return m_schema->GetMaxContiguousAllocationSize();
}
//=========================================================================
// GetSubAllocator
// [1/28/2011]
//=========================================================================
IAllocatorAllocate* BestFitExternalMapAllocator::GetSubAllocator()
{
return m_schema->GetSubAllocator();
}
} // namespace AZ
@@ -19,7 +19,6 @@ namespace AZ
*/
class BestFitExternalMapAllocator
: public AllocatorBase
, public IAllocatorAllocate
{
public:
AZ_TYPE_INFO(BestFitExternalMapAllocator, "{36266C8B-9A2C-4E3E-9812-3DB260868A2B}")
@@ -37,7 +36,7 @@ namespace AZ
static const int m_memoryBlockAlignment = 16;
void* m_memoryBlock; ///< Pointer to memory to allocate from. Can be uncached.
unsigned int m_memoryBlockByteSize; ///< Sizes if the memory block.
IAllocatorAllocate* m_mapAllocator; ///< Allocator for the free chunks map. If null the SystemAllocator will be used.
IAllocator* m_mapAllocator; ///< Allocator for the free chunks map. If null the SystemAllocator will be used.
bool m_allocationRecords; ///< True if we want to track memory allocations, otherwise false.
unsigned char m_stackRecordLevels; ///< If stack recording is enabled, how many stack levels to record.
@@ -52,7 +51,7 @@ namespace AZ
AllocatorDebugConfig GetDebugConfig() override;
//////////////////////////////////////////////////////////////////////////
// IAllocatorAllocate
// IAllocatorSchema
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
size_type Resize(pointer_type ptr, size_type newSize) override;
@@ -63,7 +62,6 @@ namespace AZ
size_type Capacity() const override;
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
IAllocatorAllocate* GetSubAllocator() override;
//////////////////////////////////////////////////////////////////////////
protected:
@@ -71,7 +69,6 @@ namespace AZ
BestFitExternalMapAllocator& operator=(const BestFitExternalMapAllocator&);
Descriptor m_desc;
BestFitExternalMapSchema* m_schema;
};
}
@@ -42,9 +42,15 @@ namespace AZ
// Allocate
// [1/28/2011]
//=========================================================================
BestFitExternalMapSchema::pointer_type BestFitExternalMapSchema::Allocate(size_type byteSize, size_type alignment, int flags)
BestFitExternalMapSchema::pointer_type BestFitExternalMapSchema::Allocate(
size_type byteSize,
size_type alignment,
[[maybe_unused]] int flags,
[[maybe_unused]] const char* name,
[[maybe_unused]] const char* fileName,
[[maybe_unused]] int lineNum,
[[maybe_unused]] unsigned int suppressStackRecord)
{
(void)flags;
char* address = nullptr;
AZ_Assert(alignment > 0 && (alignment & (alignment - 1)) == 0, "Alignment must be >0 and power of 2!");
for (int i = 0; i < 2; ++i) // max 2 attempts to allocate
@@ -96,7 +102,7 @@ namespace AZ
// DeAllocate
// [1/28/2011]
//=========================================================================
void BestFitExternalMapSchema::DeAllocate(pointer_type ptr)
void BestFitExternalMapSchema::DeAllocate(pointer_type ptr, [[maybe_unused]] size_type byteSize, [[maybe_unused]] size_type alignment)
{
if (ptr == nullptr)
{
@@ -125,6 +131,18 @@ namespace AZ
return 0;
}
BestFitExternalMapSchema::size_type BestFitExternalMapSchema::Resize(pointer_type, size_type)
{
AZ_Assert(false, "%s unsupported", AZ_FUNCTION_SIGNATURE);
return 0;
}
BestFitExternalMapSchema::pointer_type BestFitExternalMapSchema::ReAllocate(pointer_type, size_type, size_type)
{
AZ_Assert(false, "%s unsupported", AZ_FUNCTION_SIGNATURE);
return nullptr;
}
//=========================================================================
// GetMaxAllocationSize
// [1/28/2011]
@@ -20,7 +20,7 @@ namespace AZ
* External map allows us to use this allocator with uncached memory,
* because the tracking node is stored outside the main chunk.
*/
class BestFitExternalMapSchema
class BestFitExternalMapSchema : public IAllocatorSchema
{
public:
typedef void* pointer_type;
@@ -44,26 +44,27 @@ namespace AZ
static const int m_memoryBlockAlignment = 16;
void* m_memoryBlock; ///< Pointer to memory to allocate from. Can be uncached.
unsigned int m_memoryBlockByteSize; ///< Sizes if the memory block.
IAllocatorAllocate* m_mapAllocator; ///< Allocator for the free chunks map. If null the SystemAllocator will be used.
IAllocator* m_mapAllocator; ///< Allocator for the free chunks map. If null the SystemAllocator will be used.
};
BestFitExternalMapSchema(const Descriptor& desc);
pointer_type Allocate(size_type byteSize, size_type alignment, int flags);
void DeAllocate(pointer_type ptr);
size_type AllocationSize(pointer_type ptr);
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = nullptr, const char* fileName = nullptr, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
size_type Resize(pointer_type ptr, size_type newSize) override;
pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override;
size_type AllocationSize(pointer_type ptr) override;
AZ_FORCE_INLINE size_type NumAllocatedBytes() const { return m_used; }
AZ_FORCE_INLINE size_type Capacity() const { return m_desc.m_memoryBlockByteSize; }
size_type GetMaxAllocationSize() const;
size_type GetMaxContiguousAllocationSize() const;
AZ_FORCE_INLINE IAllocatorAllocate* GetSubAllocator() const { return m_desc.m_mapAllocator; }
AZ_FORCE_INLINE size_type NumAllocatedBytes() const override { return m_used; }
AZ_FORCE_INLINE size_type Capacity() const override { return m_desc.m_memoryBlockByteSize; }
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
/**
* Since we don't consolidate chucnks at free time (too expensive) we will do it as we need or when we can't
* Since we don't consolidate chunks at free time (too expensive) we will do it as we need or when we can't
* allocate memory. This function is at least O(nlogn) where 'n' are the free chunks.
*/
void GarbageCollect();
void GarbageCollect() override;
private:
AZ_FORCE_INLINE size_type ChunckSize(pointer_type ptr);
@@ -107,7 +107,6 @@ namespace AZ
m_used = 0;
m_desc = desc;
m_subAllocator = nullptr;
for (int i = 0; i < Descriptor::m_maxNumBlocks; ++i)
{
@@ -17,7 +17,7 @@ namespace AZ
* Internally uses use dlmalloc or version of it (nedmalloc, ptmalloc3).
*/
class HeapSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
typedef void* pointer_type;
@@ -52,7 +52,6 @@ namespace AZ
size_type Capacity() const override { return m_capacity; }
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
IAllocatorAllocate* GetSubAllocator() override { return m_subAllocator; }
void GarbageCollect() override {}
private:
@@ -62,7 +61,7 @@ namespace AZ
Descriptor m_desc;
size_type m_capacity; ///< Capacity in bytes.
size_type m_used; ///< Number of bytes in use.
IAllocatorAllocate* m_subAllocator;
IAllocatorSchema* m_subAllocator;
bool m_ownMemoryBlock[Descriptor::m_maxNumBlocks];
};
}
@@ -719,8 +719,12 @@ namespace AZ {
#endif // DEBUG_ALLOCATOR
size_t mTotalAllocatedSizeBuckets = 0;
size_t mTotalCapacitySizeBuckets = 0;
// Bucket-dependent counters need to atomic since the locks that protect bucket allocations are per bucket
// So multiple threads could be updating these counters
AZStd::atomic<size_t> mTotalAllocatedSizeBuckets = 0;
AZStd::atomic<size_t> mTotalCapacitySizeBuckets = 0;
// In the case of tree allocations, there is a lock on the tree, so these counters are protected from multiple
// threads through that lock
size_t mTotalAllocatedSizeTree = 0;
size_t mTotalCapacitySizeTree = 0;
public:
@@ -1081,7 +1085,7 @@ namespace AZ {
const size_t m_treePageAlignment;
const size_t m_poolPageSize;
bool m_isPoolAllocations;
IAllocatorAllocate* m_subAllocator;
IAllocatorSchema* m_subAllocator;
#if !defined (USE_MUTEX_PER_BUCKET)
mutable AZStd::mutex m_mutex;
@@ -19,7 +19,7 @@ namespace AZ
* Heap allocator schema, based on Dimitar Lazarov "High Performance Heap Allocator".
*/
class HphaSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
/**
@@ -47,7 +47,7 @@ namespace AZ
unsigned int m_isPoolAllocations : 1; ///< True to allow allocations from pools, otherwise false.
size_t m_fixedMemoryBlockByteSize; ///< Memory block size, if 0 we use the OS memory allocation functions.
void* m_fixedMemoryBlock; ///< Can be NULL if so the we will allocate memory from the subAllocator if m_memoryBlocksByteSize is != 0.
IAllocatorAllocate* m_subAllocator; ///< Allocator that m_memoryBlocks memory was allocated from or should be allocated (if NULL).
IAllocatorSchema* m_subAllocator; ///< Allocator that m_memoryBlocks memory was allocated from or should be allocated (if NULL).
size_t m_systemChunkSize; ///< Size of chunk to request from the OS when more memory is needed (defaults to m_pageSize)
size_t m_capacity; ///< Max size this allocator can grow to
};
@@ -68,7 +68,6 @@ namespace AZ
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
size_type GetUnAllocatedMemory(bool isPrint = false) const override;
IAllocatorAllocate* GetSubAllocator() override { return m_desc.m_subAllocator; }
/// Return unused memory to the OS (if we don't use fixed block). Don't call this unless you really need free memory, it is slow.
void GarbageCollect() override;
@@ -9,23 +9,12 @@
namespace AZ
{
IAllocator::IAllocator(IAllocatorAllocate* allocationSource)
: m_allocationSource(allocationSource)
, m_originalAllocationSource(allocationSource)
IAllocator::IAllocator(IAllocatorSchema* schema)
: m_schema(schema)
{
}
IAllocator::~IAllocator()
{
}
void IAllocator::SetAllocationSource(IAllocatorAllocate* allocationSource)
{
m_allocationSource = allocationSource;
}
void IAllocator::ResetAllocationSource()
{
m_allocationSource = m_originalAllocationSource;
}
}
@@ -28,19 +28,18 @@ namespace AZ
class AllocatorManager;
/**
* Allocator alloc/free basic interface. It is separate because it can be used
* for user provided allocators overrides
* Allocator schema interface
*/
class IAllocatorAllocate
class IAllocatorSchema
{
public:
typedef void* pointer_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
virtual ~IAllocatorAllocate() {}
virtual ~IAllocatorSchema() = default;
virtual pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) = 0;
virtual pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = nullptr, const char* fileName = nullptr, int lineNum = 0, unsigned int suppressStackRecord = 0) = 0;
virtual void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) = 0;
/// Resize an allocated memory block. Returns the new adjusted size (as close as possible or equal to the requested one) or 0 (if you don't support resize at all).
virtual size_type Resize(pointer_type ptr, size_type newSize) = 0;
@@ -70,8 +69,6 @@ namespace AZ
* that will be reported.
*/
virtual size_type GetUnAllocatedMemory(bool isPrint = false) const { (void)isPrint; return 0; }
/// Returns a pointer to a sub-allocator or NULL.
virtual IAllocatorAllocate* GetSubAllocator() = 0;
};
/**
@@ -100,56 +97,19 @@ namespace AZ
/**
* Interface class for all allocators.
*/
class IAllocator
class IAllocator : public IAllocatorSchema
{
public:
IAllocator(IAllocatorAllocate* allocationSource);
IAllocator(IAllocatorSchema* schema = nullptr);
virtual ~IAllocator();
// @{ Every system allocator is required to provide name this is how
// Every system allocator is required to provide name this is how
// it will be registered with the allocator manager.
virtual const char* GetName() const = 0;
virtual const char* GetDescription() const = 0;
// @}
//---------------------------------------------------------------------
// Code releating to the allocation source is made concrete within this
// interface as a performance optimization.
//---------------------------------------------------------------------
/// Returns the current allocation source, which may be used to perform memory allocations.
AZ_FORCE_INLINE IAllocatorAllocate* GetAllocationSource() const
{
return m_allocationSource;
}
/// Returns the original allocation source. Generally only used for debugging purposes.
AZ_FORCE_INLINE IAllocatorAllocate* GetOriginalAllocationSource() const
{
return m_originalAllocationSource;
}
/// Returns true if the allocation source has changed from its original value.
AZ_FORCE_INLINE bool IsAllocationSourceChanged() const
{
return m_allocationSource != m_originalAllocationSource;
}
/// Sets the allocation source, effectively overriding the allocator.
/// Be very careful doing this, as existing allocations will be deallocated through the new source,
/// typically leading to unwanted effects (such as crashes).
void SetAllocationSource(IAllocatorAllocate* allocationSource);
/// Restores the allocation source to its original value.
/// Be very careful doing this, as allocations that came from the new source will now be deallocated
/// through the original source, typically leading to unwanted effects (such as crashes).
void ResetAllocationSource();
//---------------------------------------------------------------------
/// Returns the schema, if the allocator uses one. Returns nullptr if the allocator does not use a schema.
/// This is mainly used when debugging to determine if allocators alias each other under the hood.
virtual IAllocatorAllocate* GetSchema() = 0;
/// Returns the schema
AZ_FORCE_INLINE IAllocatorSchema* GetSchema() const { return m_schema; };
/// Returns the debug configuration for this allocator.
virtual AllocatorDebugConfig GetDebugConfig() = 0;
@@ -163,11 +123,6 @@ namespace AZ
/// Returns true if this allocator is ready to use.
virtual bool IsReady() const = 0;
/// Returns true if this allocator can be overridden with a different source.
/// Almost all allocators should return true. There are very few minor exceptions, such as the OS Allocator, that are required for direct
/// interfacing with the kernel and must never be overridden under any circumstances.
virtual bool CanBeOverridden() const = 0;
/// Returns true if the allocator was lazily created. Exposed primarily for testing systems that need to verify the state of allocators.
virtual bool IsLazilyCreated() const = 0;
@@ -195,9 +150,7 @@ namespace AZ
virtual void Destroy() = 0;
protected:
// The allocation source is made a direct member of the interface as a performance optimization.
IAllocatorAllocate * m_allocationSource;
IAllocatorAllocate* m_originalAllocationSource;
IAllocatorSchema* m_schema;
template<class Allocator>
friend class AllocatorStorage::StoragePolicyBase;
@@ -22,31 +22,15 @@ namespace AZ::Internal
namespace AZ
{
static constexpr size_t DEFAULT_ALIGNMENT = sizeof(void*) * 2; // Default malloc alignment
//---------------------------------------------------------------------
// MallocSchema methods
//---------------------------------------------------------------------
MallocSchema::MallocSchema(const Descriptor& desc)
MallocSchema::MallocSchema(const Descriptor&)
: m_bytesAllocated(0)
{
if (desc.m_useAZMalloc)
{
static const int DEFAULT_ALIGNMENT = sizeof(void*) * 2; // Default malloc alignment
m_mallocFn = [](size_t byteSize)
{
return AZ_OS_MALLOC(byteSize, DEFAULT_ALIGNMENT);
};
m_freeFn = [](void* ptr)
{
AZ_OS_FREE(ptr);
};
}
else
{
m_mallocFn = &malloc;
m_freeFn = &free;
}
}
MallocSchema::~MallocSchema()
@@ -84,7 +68,7 @@ namespace AZ
((alignment > sizeof(double))
? alignment
: 0); // Malloc will align to a minimum boundary for native objects, so we only pad if aligning to a large value
void* data = (*m_mallocFn)(required);
void* data = AZ_OS_MALLOC(required, DEFAULT_ALIGNMENT);
void* result = PointerAlignUp(reinterpret_cast<void*>(reinterpret_cast<size_t>(data) + sizeof(Internal::Header)), alignment);
Internal::Header* header = PointerAlignDown<Internal::Header>(
(Internal::Header*)(reinterpret_cast<size_t>(result) - sizeof(Internal::Header)), AZStd::alignment_of<Internal::Header>::value);
@@ -112,7 +96,7 @@ namespace AZ
void* freePtr = reinterpret_cast<void*>(reinterpret_cast<size_t>(ptr) - static_cast<size_t>(header->offset));
m_bytesAllocated -= header->size;
(*m_freeFn)(freePtr);
AZ_OS_FREE(freePtr);
}
MallocSchema::pointer_type MallocSchema::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
@@ -166,11 +150,6 @@ namespace AZ
return AZ_CORE_MAX_ALLOCATOR_SIZE;
}
IAllocatorAllocate* MallocSchema::GetSubAllocator()
{
return nullptr;
}
void MallocSchema::GarbageCollect()
{
}
@@ -16,7 +16,7 @@ namespace AZ
* Uses malloc internally. Mainly intended for debugging using host operating system features.
*/
class MallocSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
AZ_TYPE_INFO("MallocSchema", "{2A21D120-A42A-484C-997C-5735DCCA5FE9}");
@@ -25,21 +25,13 @@ namespace AZ
typedef size_t size_type;
typedef ptrdiff_t difference_type;
struct Descriptor
{
Descriptor(bool useAZMalloc = true)
: m_useAZMalloc(useAZMalloc)
{
}
bool m_useAZMalloc;
};
struct Descriptor {};
MallocSchema(const Descriptor& desc = Descriptor());
virtual ~MallocSchema();
//---------------------------------------------------------------------
// IAllocatorAllocate
// IAllocatorSchema
//---------------------------------------------------------------------
pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
@@ -51,15 +43,9 @@ namespace AZ
size_type Capacity() const override;
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
IAllocatorAllocate* GetSubAllocator() override;
void GarbageCollect() override;
private:
typedef void* (*MallocFn)(size_t);
typedef void (*FreeFn)(void*);
AZStd::atomic<size_t> m_bytesAllocated;
MallocFn m_mallocFn;
FreeFn m_freeFn;
};
}
@@ -7,22 +7,8 @@
*/
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/parallel/containers/concurrent_fixed_unordered_set.h>
#include <AzCore/Memory/AllocatorManager.h>
AZ::AllocatorStorage::LazyAllocatorRef::~LazyAllocatorRef()
{
m_destructor(*m_allocator);
}
void AZ::AllocatorStorage::LazyAllocatorRef::Init(size_t size, size_t alignment, CreationFn creationFn, DestructionFn destructionFn)
{
m_allocator = AZ::AllocatorManager::CreateLazyAllocator(size, alignment, creationFn);
m_destructor = destructionFn;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// New overloads
+15 -111
View File
@@ -141,9 +141,9 @@ void* operator new[](std::size_t, const AZ::Internal::AllocatorDummy*);
*/
#define azfree(...) AZ_MACRO_SPECIALIZE(azfree_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
/// Returns allocation size, based on it's pointer \ref AZ::IAllocatorAllocate::AllocationSize.
/// Returns allocation size, based on it's pointer \ref AZ::IAllocatorSchema::AllocationSize.
#define azallocsize(_Ptr, _Allocator) AZ::AllocatorInstance< _Allocator >::Get().AllocationSize(_Ptr)
/// Returns the new expanded size or 0 if NOT supported by the allocator \ref AZ::IAllocatorAllocate::Resize.
/// Returns the new expanded size or 0 if NOT supported by the allocator \ref AZ::IAllocatorSchema::Resize.
#define azallocresize(_Ptr, _NewSize, _Allocator) AZ::AllocatorInstance< _Allocator >::Get().Resize(_Ptr, _NewSize)
namespace AZ {
@@ -521,19 +521,6 @@ namespace AZ
{
namespace AllocatorStorage
{
/// A private structure to create heap-storage for an allocator that won't expire until other static module members are destructed.
struct LazyAllocatorRef
{
using CreationFn = IAllocator*(*)(void*);
using DestructionFn = void(*)(IAllocator&);
~LazyAllocatorRef();
void Init(size_t size, size_t alignment, CreationFn creationFn, DestructionFn destructionFn);
IAllocator* m_allocator = nullptr;
DestructionFn m_destructor = nullptr;
};
/**
* A base class for all storage policies. This exists to provide access to private IAllocator methods via template friends.
*/
@@ -640,87 +627,6 @@ namespace AZ
template<class Allocator>
EnvironmentVariable<Allocator> EnvironmentStoragePolicy<Allocator>::s_allocator;
/**
* ModuleStoragePolicy stores the allocator in a static variable that is local to the module using it.
* This forces separate instances of the allocator to exist in each module, and permits lazy instantiation.
* We only tolerate this for some special allocators, primarily to maintain backwards compatibility with CryEngine,
* since it still allocates outside of code in the data section.
*
* It has two ways of storing its allocator: either on the heap, which is the preferred way, since it guarantees
* the memory for the allocator won't be deallocated (such as in a DLL) before anyone that's using it. If disabled
* the allocator is stored in a static variable, which should only be used where this isn't a problem a shut-down
* time, such as on a console.
*/
template<class Allocator, bool StoreAllocatorOnHeap>
struct ModuleStoragePolicyBase;
template<class Allocator>
struct ModuleStoragePolicyBase<Allocator, false>: public StoragePolicyBase<Allocator>
{
protected:
// Use a static instance to store the allocator. This is not recommended when the order of shut-down with the module matters, as the allocator could have its memory destroyed
// before the users of it are destroyed. The primary use case for this is allocators that need to support the CRT, as they cannot allocate from the heap.
static Allocator& GetModuleAllocatorInstance()
{
static Allocator* s_allocator = nullptr;
static typename AZStd::aligned_storage<sizeof(Allocator), AZStd::alignment_of<Allocator>::value>::type s_storage;
if (!s_allocator)
{
s_allocator = new (&s_storage) Allocator;
StoragePolicyBase<Allocator>::Create(*s_allocator, typename Allocator::Descriptor(), true);
}
return *s_allocator;
}
};
template<class Allocator>
struct ModuleStoragePolicyBase<Allocator, true> : public StoragePolicyBase<Allocator>
{
protected:
// Store-on-heap implementation uses the LazyAllocatorRef to create and destroy an allocator using heap-space so there isn't a problem with destruction order within the module.
static Allocator& GetModuleAllocatorInstance()
{
static LazyAllocatorRef s_allocator;
if (!s_allocator.m_allocator)
{
s_allocator.Init(sizeof(Allocator), AZStd::alignment_of<Allocator>::value, [](void* mem) -> IAllocator* { return new (mem) Allocator; }, &StoragePolicyBase<Allocator>::Destroy);
StoragePolicyBase<Allocator>::Create(*static_cast<Allocator*>(s_allocator.m_allocator), typename Allocator::Descriptor(), true);
}
return *static_cast<Allocator*>(s_allocator.m_allocator);
}
};
template<class Allocator, bool StoreAllocatorOnHeap = true>
class ModuleStoragePolicy : public ModuleStoragePolicyBase<Allocator, StoreAllocatorOnHeap>
{
public:
using Base = ModuleStoragePolicyBase<Allocator, StoreAllocatorOnHeap>;
static IAllocator& GetAllocator()
{
return Base::GetModuleAllocatorInstance();
}
static void Create(const typename Allocator::Descriptor& desc = typename Allocator::Descriptor())
{
StoragePolicyBase<Allocator>::Create(Base::GetModuleAllocatorInstance(), desc, true);
}
static void Destroy()
{
StoragePolicyBase<Allocator>::Destroy(Base::GetModuleAllocatorInstance());
}
static bool IsReady()
{
return Base::GetModuleAllocatorInstance().IsReady();
}
};
}
namespace Internal
@@ -734,12 +640,15 @@ namespace AZ
public:
typedef typename Allocator::Descriptor Descriptor;
AZ_FORCE_INLINE static IAllocatorAllocate& Get()
// Maintained for backwards compatibility, prefer to use Get() instead.
// Get was previously used to get the the schema, however, that bypasses what the allocators are doing.
// If the schema is needed, call Get().GetSchema()
AZ_FORCE_INLINE static IAllocator& GetAllocator()
{
return *GetAllocator().GetAllocationSource();
return StoragePolicy::GetAllocator();
}
AZ_FORCE_INLINE static IAllocator& GetAllocator()
AZ_FORCE_INLINE static IAllocator& Get()
{
return StoragePolicy::GetAllocator();
}
@@ -781,7 +690,7 @@ namespace AZ
// structure of another allocator
template <class ParentAllocator>
class ChildAllocatorSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
// No descriptor is necessary, as the parent allocator is expected to already
@@ -792,7 +701,7 @@ namespace AZ
ChildAllocatorSchema(const Descriptor&) {}
//---------------------------------------------------------------------
// IAllocatorAllocate
// IAllocatorSchema
//---------------------------------------------------------------------
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override
{
@@ -848,11 +757,6 @@ namespace AZ
{
return AZ::AllocatorInstance<Parent>::Get().GetUnAllocatedMemory(isPrint);
}
IAllocatorAllocate* GetSubAllocator() override
{
return AZ::AllocatorInstance<Parent>::Get().GetSubAllocator();
}
};
/**
@@ -873,7 +777,7 @@ namespace AZ
{
if (AllocatorInstance<Allocator>::IsReady())
{
m_name = AllocatorInstance<Allocator>::GetAllocator().GetName();
m_name = AllocatorInstance<Allocator>::Get().GetName();
}
else
{
@@ -932,7 +836,7 @@ namespace AZ
typedef AZStd::ptrdiff_t difference_type;
typedef AZStd::false_type allow_memory_leaks; ///< Regular allocators should not leak.
AZ_FORCE_INLINE AZStdIAllocator(IAllocatorAllocate* allocator, const char* name = "AZ::AZStdIAllocator")
AZ_FORCE_INLINE AZStdIAllocator(IAllocator* allocator, const char* name = "AZ::AZStdIAllocator")
: m_allocator(allocator)
, m_name(name)
{
@@ -965,7 +869,7 @@ namespace AZ
AZ_FORCE_INLINE bool operator==(const AZStdIAllocator& rhs) const { return m_allocator == rhs.m_allocator; }
AZ_FORCE_INLINE bool operator!=(const AZStdIAllocator& rhs) const { return m_allocator != rhs.m_allocator; }
private:
IAllocatorAllocate* m_allocator;
IAllocator* m_allocator;
const char* m_name;
};
@@ -982,8 +886,8 @@ namespace AZ
using size_type = AZStd::size_t;
using difference_type = AZStd::ptrdiff_t;
using allow_memory_leaks = AZStd::false_type; ///< Regular allocators should not leak.
using functor_type = IAllocatorAllocate&(*)(); ///< Function Pointer must return IAllocatorAllocate&.
///< function pointers do not support covariant return types
using functor_type = IAllocator&(*)(); ///< Function Pointer must return IAllocator&.
///< function pointers do not support covariant return types
constexpr AZStdFunctorAllocator(functor_type allocatorFunctor, const char* name = "AZ::AZStdFunctorAllocator")
: m_allocatorFunctor(allocatorFunctor)
@@ -19,7 +19,6 @@ namespace AZ
, m_custom(nullptr)
, m_numAllocatedBytes(0)
{
DisableOverriding();
}
//=========================================================================
@@ -24,7 +24,6 @@ namespace AZ
*/
class OSAllocator
: public AllocatorBase
, public IAllocatorAllocate
{
public:
AZ_TYPE_INFO(OSAllocator, "{9F835EE3-F23C-454E-B4E3-011E2F3C8118}")
@@ -39,7 +38,7 @@ namespace AZ
{
Descriptor()
: m_custom(0) {}
IAllocatorAllocate* m_custom; ///< You can provide our own allocation scheme. If NULL a HeapScheme will be used with the provided Descriptor.
IAllocatorSchema* m_custom; ///< You can provide our own allocation scheme. If NULL a HeapScheme will be used with the provided Descriptor.
};
bool Create(const Descriptor& desc);
@@ -51,7 +50,7 @@ namespace AZ
AllocatorDebugConfig GetDebugConfig() override;
//////////////////////////////////////////////////////////////////////////
// IAllocatorAllocate
// IAllocatorSchema
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
size_type Resize(pointer_type ptr, size_type newSize) override { return m_custom ? m_custom->Resize(ptr, newSize) : 0; }
@@ -62,13 +61,12 @@ namespace AZ
size_type Capacity() const override { return m_custom ? m_custom->Capacity() : AZ_CORE_MAX_ALLOCATOR_SIZE; } // custom size or unlimited
size_type GetMaxAllocationSize() const override { return m_custom ? m_custom->GetMaxAllocationSize() : AZ_CORE_MAX_ALLOCATOR_SIZE; } // custom size or unlimited
size_type GetMaxContiguousAllocationSize() const override { return m_custom ? m_custom->GetMaxContiguousAllocationSize() : AZ_CORE_MAX_ALLOCATOR_SIZE; } // custom size or unlimited
IAllocatorAllocate* GetSubAllocator() override { return m_custom ? m_custom : NULL; }
protected:
OSAllocator(const OSAllocator&);
OSAllocator& operator=(const OSAllocator&);
IAllocatorAllocate* m_custom;
IAllocatorSchema* m_custom;
size_type m_numAllocatedBytes;
};
@@ -233,7 +233,6 @@ namespace AZ
size_type Capacity() const;
size_type GetMaxAllocationSize() const;
size_type GetMaxContiguousAllocationSize() const;
IAllocatorAllocate* GetSubAllocator();
void GarbageCollect();
private:
@@ -680,11 +679,6 @@ auto AZ::OverrunDetectionSchemaImpl::GetMaxContiguousAllocationSize() const -> s
return 0;
}
AZ::IAllocatorAllocate* AZ::OverrunDetectionSchemaImpl::GetSubAllocator()
{
return nullptr;
}
void AZ::OverrunDetectionSchemaImpl::GarbageCollect()
{
}
@@ -810,11 +804,6 @@ auto AZ::OverrunDetectionSchema::GetMaxContiguousAllocationSize() const -> size_
return m_impl->GetMaxContiguousAllocationSize();
}
AZ::IAllocatorAllocate* AZ::OverrunDetectionSchema::GetSubAllocator()
{
return m_impl->GetSubAllocator();
}
void AZ::OverrunDetectionSchema::GarbageCollect()
{
m_impl->GarbageCollect();
@@ -27,7 +27,7 @@ namespace AZ
* the requested memory, plus the trap page). On most platforms this is 8kb (4kb * 2 pages).
*/
class OverrunDetectionSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
AZ_TYPE_INFO("OverrunDetectionSchema", "{0DF781AC-1615-40AE-81F7-6CA5841E2914}");
@@ -75,7 +75,7 @@ namespace AZ
virtual ~OverrunDetectionSchema();
//---------------------------------------------------------------------
// IAllocatorAllocate
// IAllocatorSchema
//---------------------------------------------------------------------
pointer_type Allocate(size_type byteSize, size_type alignment, int flags, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
@@ -87,7 +87,6 @@ namespace AZ
size_type Capacity() const override;
size_type GetMaxAllocationSize() const override;
size_type GetMaxContiguousAllocationSize() const override;
IAllocatorAllocate* GetSubAllocator() override;
void GarbageCollect() override;
private:
@@ -102,7 +102,7 @@ namespace AZ
}
//////////////////////////////////////////////////////////////////////////
// IAllocatorAllocate
// IAllocatorSchema
pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override
{
(void)ptr;
@@ -163,7 +163,7 @@ namespace AZ
}
using AllocatorType = PoolAllocation<PoolSchemaImpl>;
IAllocatorAllocate* m_pageAllocator;
IAllocatorSchema* m_pageAllocator;
AllocatorType m_allocator;
void* m_staticDataBlock;
unsigned int m_numStaticPages;
@@ -301,7 +301,7 @@ namespace AZ
FreePagesType m_freePages;
AZStd::vector<ThreadPoolData*> m_threads; ///< Array with all separate thread data. Used to traverse end free elements.
IAllocatorAllocate* m_pageAllocator;
IAllocatorSchema* m_pageAllocator;
void* m_staticDataBlock;
size_t m_numStaticPages;
size_t m_pageSize;
@@ -744,15 +744,6 @@ namespace AZ
return m_impl->m_numStaticPages * m_impl->m_pageSize;
}
//=========================================================================
// GetPageAllocator
// [11/17/2010]
//=========================================================================
IAllocatorAllocate* PoolSchema::GetSubAllocator()
{
return m_impl->m_pageAllocator;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// PollAllocator Implementation
@@ -1095,15 +1086,6 @@ namespace AZ
return m_impl->m_numStaticPages * m_impl->m_pageSize;
}
//=========================================================================
// GetPageAllocator
// [11/17/2010]
//=========================================================================
IAllocatorAllocate* ThreadPoolSchema::GetSubAllocator()
{
return m_impl->m_pageAllocator;
}
//=========================================================================
// ThreadPoolSchemaImpl
// [9/15/2009]
@@ -21,7 +21,7 @@ namespace AZ
* use ThreadPool Schema or do the sync yourself.
*/
class PoolSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
/**
@@ -51,7 +51,7 @@ namespace AZ
* this is the minimum number of pages we will have allocated at all times, otherwise the total number of pages supported.
*/
unsigned int m_numStaticPages;
IAllocatorAllocate* m_pageAllocator; ///< If you provide this interface we will use it for page allocations, otherwise SystemAllocator will be used.
IAllocatorSchema* m_pageAllocator; ///< If you provide this interface we will use it for page allocations, otherwise SystemAllocator will be used.
};
PoolSchema(const Descriptor& desc = Descriptor());
@@ -72,7 +72,6 @@ namespace AZ
size_type GetMaxContiguousAllocationSize() const override;
size_type NumAllocatedBytes() const override;
size_type Capacity() const override;
IAllocatorAllocate* GetSubAllocator() override;
protected:
PoolSchema(const PoolSchema&);
@@ -89,7 +88,7 @@ namespace AZ
* for each thread. So there will be some memory overhead, especially if you use fixed pool sizes.
*/
class ThreadPoolSchema
: public IAllocatorAllocate
: public IAllocatorSchema
{
public:
// Functions for getting an instance of a ThreadPoolData when using thread local storage
@@ -118,7 +117,6 @@ namespace AZ
size_type GetMaxContiguousAllocationSize() const override;
size_type NumAllocatedBytes() const override;
size_type Capacity() const override;
IAllocatorAllocate* GetSubAllocator() override;
protected:
ThreadPoolSchema(const ThreadPoolSchema&);
@@ -22,17 +22,15 @@ namespace AZ
template <class Schema, class DescriptorType=typename Schema::Descriptor, bool ProfileAllocations=true, bool ReportOutOfMemory=true>
class SimpleSchemaAllocator
: public AllocatorBase
, public IAllocatorAllocate
{
public:
using Descriptor = DescriptorType;
using pointer_type = typename IAllocatorAllocate::pointer_type;
using size_type = typename IAllocatorAllocate::size_type;
using difference_type = typename IAllocatorAllocate::difference_type;
using pointer_type = typename Schema::pointer_type;
using size_type = typename Schema::size_type;
using difference_type = typename Schema::difference_type;
SimpleSchemaAllocator(const char* name, const char* desc)
: AllocatorBase(this, name, desc)
, m_schema(nullptr)
: AllocatorBase(nullptr, name, desc)
{
}
@@ -65,13 +63,8 @@ namespace AZ
return AllocatorDebugConfig();
}
IAllocatorAllocate* GetSchema() override
{
return m_schema;
}
//---------------------------------------------------------------------
// IAllocatorAllocate
// IAllocatorSchema
//---------------------------------------------------------------------
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = nullptr, const char* fileName = nullptr, int lineNum = 0, unsigned int suppressStackRecord = 0) override
{
@@ -188,14 +181,6 @@ namespace AZ
{
return m_schema->GetUnAllocatedMemory(isPrint);
}
IAllocatorAllocate* GetSubAllocator() override
{
return m_schema->GetSubAllocator();
}
protected:
IAllocatorAllocate* m_schema;
private:
typename AZStd::aligned_storage<sizeof(Schema), AZStd::alignment_of<Schema>::value>::type m_schemaStorage;
@@ -50,9 +50,8 @@ namespace AZ
// [9/2/2009]
//=========================================================================
SystemAllocator::SystemAllocator()
: AllocatorBase(this, "SystemAllocator", "Fundamental generic memory allocator")
: AllocatorBase(nullptr, "SystemAllocator", "Fundamental generic memory allocator")
, m_isCustom(false)
, m_allocator(nullptr)
, m_ownsOSAllocator(false)
{
}
@@ -91,7 +90,7 @@ namespace AZ
if (desc.m_custom)
{
m_isCustom = true;
m_allocator = desc.m_custom;
m_schema = desc.m_custom;
isReady = true;
}
else
@@ -119,9 +118,9 @@ namespace AZ
AZ_Assert(!g_isSystemSchemaUsed, "AZ::SystemAllocator MUST be created first! It's the source of all allocations!");
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
m_allocator = new (&g_systemSchema) HphaSchema(heapDesc);
m_schema = new (&g_systemSchema) HphaSchema(heapDesc);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = new (&g_systemSchema) MallocSchema(heapDesc);
m_schema = new (&g_systemSchema) MallocSchema(heapDesc);
#endif
g_isSystemSchemaUsed = true;
isReady = true;
@@ -134,11 +133,11 @@ namespace AZ
"System allocator must be created before any other allocator! They allocate from it.");
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
m_allocator = azcreate(HphaSchema, (heapDesc), SystemAllocator);
m_schema = azcreate(HphaSchema, (heapDesc), SystemAllocator);
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
m_allocator = azcreate(MallocSchema, (heapDesc), SystemAllocator);
m_schema = azcreate(MallocSchema, (heapDesc), SystemAllocator);
#endif
if (m_allocator == nullptr)
if (m_schema == nullptr)
{
isReady = false;
}
@@ -166,18 +165,18 @@ namespace AZ
if (!m_isCustom)
{
if ((void*)m_allocator == (void*)&g_systemSchema)
if ((void*)m_schema == (void*)&g_systemSchema)
{
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
static_cast<HphaSchema*>(m_allocator)->~HphaSchema();
static_cast<HphaSchema*>(m_schema)->~HphaSchema();
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
static_cast<MallocSchema*>(m_schema)->~MallocSchema();
#endif
g_isSystemSchemaUsed = false;
}
else
{
azdestroy(m_allocator);
azdestroy(m_schema);
}
}
@@ -197,11 +196,6 @@ namespace AZ
.ExcludeFromDebugging(!m_desc.m_allocationRecords);
}
IAllocatorAllocate* SystemAllocator::GetSchema()
{
return m_allocator;
}
//=========================================================================
// Allocate
// [9/2/2009]
@@ -224,14 +218,14 @@ namespace AZ
byteSize = MemorySizeAdjustedUp(byteSize);
SystemAllocator::pointer_type address =
m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
m_schema->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
if (address == nullptr)
{
// Free all memory we can and try again!
AllocatorManager::Instance().GarbageCollect();
address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
address = m_schema->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
}
if (address == nullptr)
@@ -258,7 +252,7 @@ namespace AZ
byteSize = MemorySizeAdjustedUp(byteSize);
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
m_allocator->DeAllocate(ptr, byteSize, alignment);
m_schema->DeAllocate(ptr, byteSize, alignment);
}
//=========================================================================
@@ -271,7 +265,7 @@ namespace AZ
AZ_MEMORY_PROFILE(ProfileReallocationBegin(ptr, newSize));
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
pointer_type newAddress = m_allocator->ReAllocate(ptr, newSize, newAlignment);
pointer_type newAddress = m_schema->ReAllocate(ptr, newSize, newAlignment);
AZ_PROFILE_MEMORY_ALLOC(MemoryReserved, newAddress, newSize, "SystemAllocator realloc");
AZ_MEMORY_PROFILE(ProfileReallocationEnd(ptr, newAddress, newSize, newAlignment));
@@ -285,7 +279,7 @@ namespace AZ
SystemAllocator::size_type SystemAllocator::Resize(pointer_type ptr, size_type newSize)
{
newSize = MemorySizeAdjustedUp(newSize);
size_type resizedSize = m_allocator->Resize(ptr, newSize);
size_type resizedSize = m_schema->Resize(ptr, newSize);
AZ_MEMORY_PROFILE(ProfileResize(ptr, resizedSize));
@@ -298,7 +292,7 @@ namespace AZ
//=========================================================================
SystemAllocator::size_type SystemAllocator::AllocationSize(pointer_type ptr)
{
size_type allocSize = MemorySizeAdjustedDown(m_allocator->AllocationSize(ptr));
size_type allocSize = MemorySizeAdjustedDown(m_schema->AllocationSize(ptr));
return allocSize;
}
@@ -25,7 +25,6 @@ namespace AZ
*/
class SystemAllocator
: public AllocatorBase
, public IAllocatorAllocate
{
public:
AZ_TYPE_INFO(SystemAllocator, "{424C94D8-85CF-4E89-8CD6-AB5EC173E875}")
@@ -38,7 +37,7 @@ namespace AZ
* we will allocate system memory using system calls. You can
* provide arenas (spaces) with pre-allocated memory, and use the
* flag to specify which arena you want to allocate from.
* You are also allowed to supply IAllocatorAllocate, but if you do
* You are also allowed to supply IAllocatorSchema, but if you do
* so you will need to take care of all allocations, we will not use
* the default HeapSchema.
* \ref HeapSchema::Descriptor
@@ -50,7 +49,7 @@ namespace AZ
, m_allocationRecords(true)
, m_stackRecordLevels(5)
{}
IAllocatorAllocate* m_custom; ///< You can provide our own allocation scheme. If NULL a HeapScheme will be used with the provided Descriptor.
IAllocatorSchema* m_custom; ///< You can provide our own allocation scheme. If NULL a HeapScheme will be used with the provided Descriptor.
struct Heap
{
@@ -72,7 +71,7 @@ namespace AZ
int m_numFixedMemoryBlocks; ///< Number of memory blocks to use.
void* m_fixedMemoryBlocks[m_maxNumFixedBlocks]; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
size_t m_fixedMemoryBlocksByteSize[m_maxNumFixedBlocks]; ///< Sizes of different memory blocks (MUST be multiple of m_pageSize), if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
IAllocatorAllocate* m_subAllocator; ///< Allocator that m_memoryBlocks memory was allocated from or should be allocated (if NULL).
IAllocatorSchema* m_subAllocator; ///< Allocator that m_memoryBlocks memory was allocated from or should be allocated (if NULL).
size_t m_systemChunkSize; ///< Size of chunk to request from the OS when more memory is needed (defaults to m_pageSize)
} m_heap;
bool m_allocationRecords; ///< True if we want to track memory allocations, otherwise false.
@@ -86,25 +85,23 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
// IAllocator
AllocatorDebugConfig GetDebugConfig() override;
IAllocatorAllocate* GetSchema() override;
//////////////////////////////////////////////////////////////////////////
// IAllocatorAllocate
// IAllocatorSchema
pointer_type Allocate(size_type byteSize, size_type alignment, int flags = 0, const char* name = 0, const char* fileName = 0, int lineNum = 0, unsigned int suppressStackRecord = 0) override;
void DeAllocate(pointer_type ptr, size_type byteSize = 0, size_type alignment = 0) override;
pointer_type ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment) override;
size_type Resize(pointer_type ptr, size_type newSize) override;
size_type AllocationSize(pointer_type ptr) override;
void GarbageCollect() override { m_allocator->GarbageCollect(); }
void GarbageCollect() override { GetSchema()->GarbageCollect(); }
size_type NumAllocatedBytes() const override { return m_allocator->NumAllocatedBytes(); }
size_type Capacity() const override { return m_allocator->Capacity(); }
size_type NumAllocatedBytes() const override { return GetSchema()->NumAllocatedBytes(); }
size_type Capacity() const override { return GetSchema()->Capacity(); }
/// Keep in mind this operation will execute GarbageCollect to make sure it returns, max allocation. This function WILL be slow.
size_type GetMaxAllocationSize() const override { return m_allocator->GetMaxAllocationSize(); }
size_type GetMaxContiguousAllocationSize() const override { return m_allocator->GetMaxContiguousAllocationSize(); }
size_type GetUnAllocatedMemory(bool isPrint = false) const override { return m_allocator->GetUnAllocatedMemory(isPrint); }
IAllocatorAllocate* GetSubAllocator() override { return m_isCustom ? m_allocator : m_allocator->GetSubAllocator(); }
size_type GetMaxAllocationSize() const override { return GetSchema()->GetMaxAllocationSize(); }
size_type GetMaxContiguousAllocationSize() const override { return GetSchema()->GetMaxContiguousAllocationSize(); }
size_type GetUnAllocatedMemory(bool isPrint = false) const override { return GetSchema()->GetUnAllocatedMemory(isPrint); }
//////////////////////////////////////////////////////////////////////////
@@ -114,7 +111,6 @@ namespace AZ
Descriptor m_desc;
bool m_isCustom;
IAllocatorAllocate* m_allocator;
bool m_ownsOSAllocator;
};
}
+4 -4
View File
@@ -248,14 +248,14 @@
#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
#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
#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
#define az_has_builtin_is_constant_evaluated true
#endif
#endif
@@ -271,7 +271,7 @@
}
}
#define az_builtin_is_constant_evaluated() AZ::Internal::builtin_is_constant_evaluated()
#define az_has_builtin_is_constant_evaluated() false
#define az_has_builtin_is_constant_evaluated false
#endif
// define builtin functions used by char_traits class for efficient compile time and runtime
@@ -1457,7 +1457,7 @@ namespace AZ
static void* LuaMemoryHook(void* userData, void* ptr, size_t osize, size_t nsize)
{
(void)osize;
IAllocatorAllocate* allocator = reinterpret_cast<IAllocatorAllocate*>(userData);
IAllocator* allocator = reinterpret_cast<IAllocator*>(userData);
if (nsize == 0)
{
if (ptr)
@@ -4276,7 +4276,7 @@ LUA_API const Node* lua_getDummyNode()
AZ_CLASS_ALLOCATOR(ScriptContextImpl, AZ::SystemAllocator, 0);
//////////////////////////////////////////////////////////////////////////
ScriptContextImpl(ScriptContext* owner, IAllocatorAllocate* allocator, lua_State* nativeContext)
ScriptContextImpl(ScriptContext* owner, IAllocator* allocator, lua_State* nativeContext)
: m_owner(owner)
, m_context(nullptr)
, m_debug(nullptr)
@@ -5828,7 +5828,7 @@ LUA_API const Node* lua_getDummyNode()
AZStd::thread::id m_ownerThreadId; // Check if Lua methods (including EBus handlers) are called from background threads.
};
ScriptContext::ScriptContext(ScriptContextId id, IAllocatorAllocate* allocator, lua_State* nativeContext)
ScriptContext::ScriptContext(ScriptContextId id, IAllocator* allocator, lua_State* nativeContext)
{
m_id = id;
m_impl = aznew ScriptContextImpl(this, allocator, nativeContext);
@@ -821,7 +821,7 @@ namespace AZ
CustomFromLua m_fromLua;
};
ScriptContext(ScriptContextId id = ScriptContextIds::DefaultScriptContextId, IAllocatorAllocate* allocator = nullptr, lua_State* nativeContext = nullptr);
ScriptContext(ScriptContextId id = ScriptContextIds::DefaultScriptContextId, IAllocator* allocator = nullptr, lua_State* nativeContext = nullptr);
~ScriptContext();
/// Bind LUA context (VM) a specific behaviorContext
@@ -74,7 +74,7 @@ namespace AZ
* But as the AZStdAssociativeContainer instance will not be accessed outside of the module it was
* created within then this will return this .dll/.exe module allocator
*/
classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return GetCurrentSerializeContextModule().GetAllocator(); }));
classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocator& { return GetCurrentSerializeContextModule().GetAllocator(); }));
// Flag the field with the EnumType attribute if we're an enumeration type aliased by RemoveEnum
const bool isSpecializedEnum = AZStd::is_enum<ValueType>::value && !AzTypeInfo<ValueType>::Uuid().IsNull();
@@ -650,7 +650,7 @@ namespace AZ
* But as the AZStdAssociativeContainer instance will not be accessed outside of the module it was
* created within then this will return this .dll/.exe module allocator
*/
m_classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return GetCurrentSerializeContextModule().GetAllocator(); }));
m_classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocator& { return GetCurrentSerializeContextModule().GetAllocator(); }));
m_classElement.m_attributes.emplace_back(AZ_CRC("KeyType", 0x15bc5303), CreateModuleAttribute(AZStd::move(uuid)));
}
@@ -1668,9 +1668,23 @@ namespace AZ
SerializeContext::ENUM_ACCESS_FOR_READ,
&m_errorLogger
);
if (objectStreamWriteOverrideCB.Invoke<void>(callContext, objectPtr, *classData, classElement))
if (ObjectStreamWriteOverrideResponse writeResponse;
objectStreamWriteOverrideCB.Read<ObjectStreamWriteOverrideResponse>(writeResponse, callContext, objectPtr, *classData, classElement))
{
return false;
switch (writeResponse)
{
case ObjectStreamWriteOverrideResponse::FallbackToDefaultWrite:
break;
case ObjectStreamWriteOverrideResponse::AbortWrite:
m_errorLogger.ReportError(AZStd::string::format("ObjectStream Write Element Override callback has aborted the write for class data %s",
classData->m_name).c_str());
[[fallthrough]];
case ObjectStreamWriteOverrideResponse::CompletedWrite:
return false;
default:
AZ_Error("Serialize", false, "Invalid Response %d returned from the ObjectStream Write Element Override callback", static_cast<int>(writeResponse));
return false;
}
}
else
{
@@ -49,14 +49,24 @@ namespace AZ
static const AZ::Crc32 ObjectStreamWriteElementOverride = AZ_CRC("ObjectStreamWriteElementOverride", 0x35eb659f);
}
enum class ObjectStreamWriteOverrideResponse
{
CompletedWrite,
FallbackToDefaultWrite,
AbortWrite
};
AZ_TYPE_INFO_SPECIALIZE(ObjectStreamWriteOverrideResponse, "{BDF960A8-0F18-4E9D-96DA-F800A122C42D}");
///< Callback that the object stream invokes to override saving an instance of the registered class
///< @param callContext EnumerateInstanceCallContext which contains the WriteElement BeingElemCB and the CloseElement EndElemCB
///< the callContext parameter can be passed to the SerializeContext::EnumerateInstance to continue object stream writing
///< @param classPtr class type which is of pointer to the type represented by the m_typeId value
///< @param classData reference to this instance Class Data that will be supplied to the callback
///< @param classElement class element pointer which contains information about the element being serialized.
///< root elements do not not have a valid class element pointer
using ObjectStreamWriteOverrideCB = AZStd::function<void(SerializeContext::EnumerateInstanceCallContext& callContext,
///< root elements have a nullptr classElement
///< @return enum to indicate that the override has saved the registered class and that the default writing should be skipped.
///< Returning false will have the WriteElement code fallback to using the default logic
using ObjectStreamWriteOverrideCB = AZStd::function<ObjectStreamWriteOverrideResponse(SerializeContext::EnumerateInstanceCallContext& callContext,
const void* classPtr, const SerializeContext::ClassData& classData, const SerializeContext::ClassElement* classElement)>;
AZ_TYPE_INFO_SPECIALIZE(ObjectStreamWriteOverrideCB, "{87B1A36B-8C8A-42B6-A0B5-E770D9FDBAD4}");
@@ -3245,7 +3245,7 @@ namespace AZ
return genericClassInfoFoundIt != m_moduleLocalGenericClassInfos.end() ? genericClassInfoFoundIt->second : nullptr;
}
AZ::IAllocatorAllocate& SerializeContext::PerModuleGenericClassInfo::GetAllocator()
AZ::IAllocator& SerializeContext::PerModuleGenericClassInfo::GetAllocator()
{
return m_moduleOSAllocator;
}
@@ -574,10 +574,10 @@ namespace AZ
GenericClassInfo* m_genericClassInfo = nullptr; ///< Valid when the generic class is set. So you don't search for the actual type in the class register.
Edit::ElementData* m_editData{}; ///< Pointer to edit data (generated by EditContext).
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator> m_attributes{
AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return AZ::AllocatorInstance<AZ::SystemAllocator>::Get(); })
AZStdFunctorAllocator([]() -> IAllocator& { return AZ::AllocatorInstance<AZ::SystemAllocator>::Get(); })
}; ///< Attributes attached to ClassElement. Lambda is required here as AZStdFunctorAllocator expects a function pointer
///< that returns an IAllocatorAllocate& and the AZ::AllocatorInstance<AZ::SystemAllocator>::Get returns an AZ::SystemAllocator&
/// which while it inherits from IAllocatorAllocate, does not work as function pointers do not support covariant return types
///< that returns an IAllocator& and the AZ::AllocatorInstance<AZ::SystemAllocator>::Get returns an AZ::SystemAllocator&
/// which while it inherits from IAllocator, does not work as function pointers do not support covariant return types
AttributeOwnership m_attributeOwnership = AttributeOwnership::Parent;
int m_flags{}; ///<
};
@@ -639,12 +639,12 @@ namespace AZ
DataPatchUpgradeHandler m_dataPatchUpgrader;
///< Attributes for this class type. Lambda is required here as AZStdFunctorAllocator expects a function pointer
///< that returns an IAllocatorAllocate& and the AZ::AllocatorInstance<AZ::SystemAllocator>::Get returns an AZ::SystemAllocator&
/// which while it inherits from IAllocatorAllocate, does not work as function pointers do not support covariant return types
///< that returns an IAllocator& and the AZ::AllocatorInstance<AZ::SystemAllocator>::Get returns an AZ::SystemAllocator&
/// which while it inherits from IAllocator, does not work as function pointers do not support covariant return types
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator> m_attributes{AZStdFunctorAllocator(&GetSystemAllocator) };
private:
static IAllocatorAllocate& GetSystemAllocator()
static IAllocator& GetSystemAllocator()
{
return AZ::AllocatorInstance<AZ::SystemAllocator>::Get();
}
@@ -2483,7 +2483,7 @@ namespace AZ
PerModuleGenericClassInfo();
~PerModuleGenericClassInfo();
AZ::IAllocatorAllocate& GetAllocator();
AZ::IAllocator& GetAllocator();
void AddGenericClassInfo(AZ::GenericClassInfo* genericClassInfo);
void RemoveGenericClassInfo(const AZ::TypeId& canonicalTypeId);
@@ -2546,12 +2546,12 @@ namespace AZ
template <typename T, typename ContainerType>
AttributePtr CreateModuleAttribute(T&& attrValue)
{
IAllocatorAllocate& moduleAllocator = GetCurrentSerializeContextModule().GetAllocator();
IAllocator& moduleAllocator = GetCurrentSerializeContextModule().GetAllocator();
void* rawMemory = moduleAllocator.Allocate(sizeof(ContainerType), alignof(ContainerType));
new (rawMemory) ContainerType{ AZStd::forward<T>(attrValue) };
auto attributeDeleter = [](Attribute* attribute)
{
IAllocatorAllocate& moduleAllocator = GetCurrentSerializeContextModule().GetAllocator();
IAllocator& moduleAllocator = GetCurrentSerializeContextModule().GetAllocator();
attribute->~Attribute();
moduleAllocator.DeAllocate(attribute);
};
@@ -12,6 +12,8 @@
namespace AZ
{
enum class ObjectStreamWriteOverrideResponse;
namespace VariantSerializationInternal
{
template <class ValueType>
@@ -32,7 +34,7 @@ namespace AZ
* But as the AZStdAssociativeContainer instance will not be accessed outside of the module it was
* created within then this will return this .dll/.exe module allocator
*/
classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocatorAllocate& { return GetCurrentSerializeContextModule().GetAllocator(); }));
classElement.m_attributes.set_allocator(AZStdFunctorAllocator([]() -> IAllocator& { return GetCurrentSerializeContextModule().GetAllocator(); }));
}
template<size_t Index, size_t... Digits>
@@ -429,7 +431,7 @@ namespace AZ
// the serialize context dll module allocator has to be used to manage the lifetime of the ClassData attributes within a module
// If a module which reflects a variant is unloaded, then the dll module allocator will properly unreflect the variant type from the serialize context
// for this particular module
AZStdFunctorAllocator dllAllocator([]() -> IAllocatorAllocate& { return GetCurrentSerializeContextModule().GetAllocator(); });
AZStdFunctorAllocator dllAllocator([]() -> IAllocator& { return GetCurrentSerializeContextModule().GetAllocator(); });
m_classData.m_attributes.set_allocator(AZStd::move(dllAllocator));
// Create the ObjectStreamWriteOverrideCB in the current module
@@ -480,7 +482,7 @@ namespace AZ
}
}
private:
static void ObjectStreamWriter(SerializeContext::EnumerateInstanceCallContext& callContext, const void* variantPtr,
static ObjectStreamWriteOverrideResponse ObjectStreamWriter(SerializeContext::EnumerateInstanceCallContext& callContext, const void* variantPtr,
[[maybe_unused]] const SerializeContext::ClassData& variantClassData, const SerializeContext::ClassElement* variantClassElement)
{
auto alternativeVisitor = [&callContext, variantClassElement](auto&& elementAlt)
@@ -503,6 +505,9 @@ namespace AZ
};
AZStd::visit(AZStd::move(alternativeVisitor), *reinterpret_cast<const VariantType*>(variantPtr));
// To avoid including ObjectStream.h into this file, we static cast the value of 0
// to an AZ::ObjectStreamWriteElemntResponse which corresponds to the CompletedWrite enum value
return static_cast<AZ::ObjectStreamWriteOverrideResponse>(0);
}
VariantSerializationInternal::AZStdVariantContainer<Types...> m_variantContainer;
@@ -1,49 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "TimeDataStatisticsManager.h"
namespace AZ
{
namespace Statistics
{
void TimeDataStatisticsManager::PushTimeDataSample(const char * registerName, const AZ::Debug::ProfilerRegister::TimeData& timeData)
{
const AZStd::string statName(registerName);
NamedRunningStatistic* statistic = GetStatistic(statName);
if (!statistic)
{
const AZStd::string units("us");
AddStatistic(statName, statName, units, false);
AZ::Debug::ProfilerRegister::TimeData zeroTimeData;
memset(&zeroTimeData, 0, sizeof(AZ::Debug::ProfilerRegister::TimeData));
m_previousTimeData[statName] = zeroTimeData;
statistic = GetStatistic(statName);
AZ_Assert(statistic != nullptr, "Fatal error adding a new statistic object");
}
const AZ::u64 accumulatedTime = timeData.m_time;
const AZ::s64 totalNumCalls = timeData.m_calls;
const AZ::u64 previousAccumulatedTime = m_previousTimeData[statName].m_time;
const AZ::s64 previousTotalNumCalls = m_previousTimeData[statName].m_calls;
const AZ::u64 deltaTime = accumulatedTime - previousAccumulatedTime;
const AZ::s64 deltaCalls = totalNumCalls - previousTotalNumCalls;
if (deltaCalls == 0)
{
//This is the same old data. Let's skip it
return;
}
double newSample = static_cast<double>(deltaTime) / deltaCalls;
statistic->PushSample(newSample);
m_previousTimeData[statName] = timeData;
}
} //namespace Statistics
} //namespace AZ
@@ -1,51 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Statistics/StatisticsManager.h>
namespace AZ
{
namespace Statistics
{
/**
* @brief Specialization useful for data generated with AZ::Debug::FrameProfileComponent
*
* Timer based data collection using AZ_PROFILE_TIMER(...), available in
* AzCore/Debug/Profiler.h can be collected when using AZ::Debug::FrameProfilerComponent
* and AZ::Debug::FrameProfilerBus. The method PushTimeDataSample(...) is a convenience
* to convert those Timer registers into a RunningStatistic.
*
*
*/
class TimeDataStatisticsManager : public StatisticsManager<>
{
public:
TimeDataStatisticsManager() = default;
virtual ~TimeDataStatisticsManager() = default;
/**
* @brief Adds one sample data to a specific running stat by name.
*
* This method is specialized to work with ProfilerRegister::TimeData that can be intercepted
* during AZ::Debug::FrameProfilerBus::OnFrameProfilerData().
* For each @param registerName a new RunningStat object is created if it doesn't exist.
*
* Adds the TimeData as one sample for its RunningStatistic.
*/
void PushTimeDataSample(const char * registerName, const AZ::Debug::ProfilerRegister::TimeData& timeData);
protected:
///We store here the previous value from the previous timer frame data.
///This is necessary because AZ_PROFILER_TIMER is cumulative
///and we need the time spent for each call.
AZStd::unordered_map<AZStd::string, AZ::Debug::ProfilerRegister::TimeData> m_previousTimeData;
};
} //namespace Statistics
} //namespace AZ
@@ -240,7 +240,6 @@ set(FILES
Jobs/JobManagerComponent.cpp
Jobs/JobManagerComponent.h
Jobs/JobManagerDesc.h
Jobs/LegacyJobExecutor.h
Jobs/MultipleDependentJob.h
Jobs/task_group.h
Math/Aabb.cpp
@@ -369,8 +368,6 @@ set(FILES
Memory/AllocatorBase.h
Memory/AllocatorManager.cpp
Memory/AllocatorManager.h
Memory/AllocatorOverrideShim.cpp
Memory/AllocatorOverrideShim.h
Memory/AllocatorWrapper.h
Memory/AllocatorScope.h
Memory/BestFitExternalMapAllocator.cpp
@@ -19,6 +19,7 @@ set(FILES
any.h
base.h
config.h
concepts/concepts.h
createdestroy.h
docs.h
exceptions.h
@@ -27,11 +28,14 @@ set(FILES
hash.cpp
hash.h
hash_table.h
iterator/iterator_primitives.h
iterator.h
limits.h
numeric.h
math.h
optional.h
ranges/iter_move.h
ranges/ranges.h
ratio.h
reference_wrapper.h
sort.h
@@ -151,6 +155,7 @@ set(FILES
typetraits/alignment_of.h
typetraits/config.h
typetraits/common_type.h
typetraits/common_reference.h
typetraits/conjunction.h
typetraits/disjunction.h
typetraits/extent.h
@@ -217,4 +222,6 @@ set(FILES
typetraits/void_t.h
typetraits/internal/type_sequence_traits.h
typetraits/internal/is_template_copy_constructible.h
utility/declval.h
utility/move.h
)
+2
View File
@@ -30,4 +30,6 @@ namespace AZStd
using std::nullptr_t;
using sys_time_t = AZ::s64;
using std::byte;
}
@@ -0,0 +1,840 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/function/invoke.h>
#include <AzCore/std/iterator/iterator_primitives.h>
#include <AzCore/std/ranges/iter_move.h>
#include <AzCore/std/typetraits/add_pointer.h>
#include <AzCore/std/typetraits/common_reference.h>
#include <AzCore/std/typetraits/extent.h>
#include <AzCore/std/typetraits/is_array.h>
#include <AzCore/std/typetraits/is_assignable.h>
#include <AzCore/std/typetraits/is_class.h>
#include <AzCore/std/typetraits/is_constructible.h>
#include <AzCore/std/typetraits/is_destructible.h>
#include <AzCore/std/typetraits/is_enum.h>
#include <AzCore/std/typetraits/is_floating_point.h>
#include <AzCore/std/typetraits/is_function.h>
#include <AzCore/std/typetraits/is_integral.h>
#include <AzCore/std/typetraits/is_object.h>
#include <AzCore/std/typetraits/is_same.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <AzCore/std/typetraits/is_void.h>
#include <AzCore/std/typetraits/remove_cvref.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/utility/declval.h>
#include <AzCore/std/utility/move.h>
namespace AZStd
{
// alias std::pointer_traits into the AZStd::namespace
using std::pointer_traits;
// Alias re-declarations from iterator.h
/// Identifying tag for input iterators.
using input_iterator_tag = std::input_iterator_tag;
/// Identifying tag for output iterators.
using output_iterator_tag = std::output_iterator_tag;
/// Identifying tag for forward iterators.
using forward_iterator_tag = std::forward_iterator_tag;
/// Identifying tag for bidirectional iterators.
using bidirectional_iterator_tag = std::bidirectional_iterator_tag;
/// Identifying tag for random-access iterators.
using random_access_iterator_tag = std::random_access_iterator_tag;
/// Identifying tag for contagious iterators
struct contiguous_iterator_tag;
}
namespace AZStd::Internal
{
template <typename T, typename = void>
constexpr bool pointer_traits_has_to_address_v = false;
template <typename T>
constexpr bool pointer_traits_has_to_address_v<T, enable_if_t<
is_void_v<void_t<decltype(pointer_traits<T>::to_address(declval<const T&>()))>>> > = true;
// pointer_traits isn't SFINAE friendly https://cplusplus.github.io/LWG/lwg-active.html#3545
// So working around that by checking if type T has an element_type alias
template <typename T, typename = void>
constexpr bool pointer_traits_valid_and_has_to_address_v = false;
template <typename T>
constexpr bool pointer_traits_valid_and_has_to_address_v<T, enable_if_t<has_element_type_v<T>> >
= pointer_traits_has_to_address_v<T>;
}
namespace AZStd
{
//! Implements the C++20 to_address function
//! This obtains the address represented by ptr without forming a reference
//! to the pointee type
template <typename T>
constexpr T* to_address(T* ptr) noexcept
{
static_assert(!AZStd::is_function_v<T>, "Invoking to address on a function pointer is not allowed");
return ptr;
}
//! Fancy pointer overload which delegates to using a specialization of pointer_traits<T>::to_address
//! if that is a well-formed expression, otherwise it returns ptr->operator->()
//! For example invoking `to_address(AZStd::reverse_iterator<const char*>(char_ptr))`
//! Returns an element of type const char*
template <typename T>
constexpr auto to_address(const T& ptr) noexcept
{
if constexpr (AZStd::Internal::pointer_traits_valid_and_has_to_address_v<T>)
{
return pointer_traits<T>::to_address(ptr);
}
else
{
return to_address(ptr.operator->());
}
}
}
namespace AZStd::Internal
{
// Variadic template which maps types to true For SFINAE
template <class... Args>
constexpr bool sfinae_trigger_v = true;
template <class It, class = void>
constexpr bool is_class_or_enum = false;
template <class It>
constexpr bool is_class_or_enum<It, enable_if_t<
(is_class_v<remove_cvref_t<It>> || is_enum_v<remove_cvref_t<It>>)>> = true;
template<class LHS, class RHS, class = void>
constexpr bool assignable_from_impl = false;
template<class LHS, class RHS>
constexpr bool assignable_from_impl<LHS, RHS, enable_if_t<is_lvalue_reference_v<LHS>
&& common_reference_with<const remove_reference_t<LHS>&, const remove_reference_t<RHS>&>
&& same_as<decltype(declval<LHS>() = declval<RHS>()), LHS> >> = true;
template<class T, class U, class = void>
constexpr bool common_with_impl = false;
template<class T, class U>
constexpr bool common_with_impl<T, U, enable_if_t<
same_as<common_type_t<T, U>, common_type_t<U, T>>
&& sfinae_trigger_v<decltype(static_cast<common_type_t<T, U>>(declval<T>()))>
&& sfinae_trigger_v<decltype(static_cast<common_type_t<T, U>>(declval<U>()))>
&& common_reference_with<add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>>
&& common_reference_with<add_lvalue_reference_t<common_type_t<T, U>>, common_reference_t<add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>>>
>> = true;
}
namespace AZStd
{
template<class T, class U>
/*concept*/ constexpr bool common_with = Internal::common_with_impl<T, U>;
template<class LHS, class RHS>
/*concept*/ constexpr bool assignable_from = Internal::assignable_from_impl<LHS, RHS>;
template<class T, class... Args>
/*concept*/ constexpr bool constructible_from = destructible<T> && is_constructible_v<T, Args...>;
template<class T>
/*concept*/ constexpr bool move_constructible = constructible_from<T, T> && convertible_to<T, T>;
template<class Derived, class Base>
/*concept*/ constexpr bool derived_from = is_base_of_v<Base, Derived> && is_convertible_v<const volatile Derived*, const volatile Base*>;
}
namespace AZStd::ranges::Internal
{
template <class T, class U, class = void>
constexpr bool is_class_or_enum_with_swap_adl = false;
template <class T, class U>
constexpr bool is_class_or_enum_with_swap_adl<T, U, enable_if_t<
(is_class_v<remove_cvref_t<T>> || is_enum_v<remove_cvref_t<T>>
|| is_class_v<remove_cvref_t<U>> || is_enum_v<remove_cvref_t<T>>)
&& is_void_v<void_t<decltype(swap(declval<T&>(), declval<U&>()))>>
>> = true;
template <class T>
void swap(T&, T&) = delete;
struct swap_fn
{
template <class T, class U>
constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept(swap(AZStd::forward<T>(t), AZStd::forward<U>(u))))
->enable_if_t<is_class_or_enum_with_swap_adl<T, U>>
{
swap(AZStd::forward<T>(t), AZStd::forward<U>(u));
}
// ranges::swap customization point https://eel.is/c++draft/concepts#concept.swappable-2.2
// Implemented in ranges.h as to prevent circular dependency.
// ranges::swap_ranges depends on the range concepts that can't be defined here
template <class T, class U>
constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept((*this)(*t, *u)))
->enable_if_t<!is_class_or_enum_with_swap_adl<T, U>
&& is_array_v<T> && is_array_v<U> && (extent_v<T> == extent_v<U>)
>;
template <class T>
constexpr auto operator()(T& t1, T& t2) const noexcept(noexcept(is_nothrow_move_constructible_v<T>&& is_nothrow_move_assignable_v<T>))
->enable_if_t<move_constructible<T>&& assignable_from<T&, T>>
{
auto temp(AZStd::move(t1));
t1 = AZStd::move(t2);
t2 = AZStd::move(temp);
}
};
}
namespace AZStd::ranges
{
inline namespace customization_point_object
{
inline constexpr auto swap = Internal::swap_fn{};
}
}
namespace AZStd::Internal
{
template <class T, class = void>
constexpr bool swappable_impl = false;
template <class T>
constexpr bool swappable_impl<T, void_t<decltype(AZStd::ranges::swap(declval<T&>(), declval<T&>()))>> = true;
template <class T, class U, class = void>
constexpr bool swappable_with_impl = false;
template <class T, class U>
constexpr bool swappable_with_impl<T, U, enable_if_t<common_reference_with<T, U>
&& sfinae_trigger_v<
decltype(AZStd::ranges::swap(declval<T&>(), declval<T&>())),
decltype(AZStd::ranges::swap(declval<U&>(), declval<U&>())),
decltype(AZStd::ranges::swap(declval<T&>(), declval<U&>())),
decltype(AZStd::ranges::swap(declval<U&>(), declval<T&>()))>>> = true;
}
namespace AZStd
{
template <class T>
/*concept*/ constexpr bool signed_integral = integral<T> && is_signed_v<T>;
template <class T>
/*concept*/ constexpr bool unsigned_integral = integral<T> && !signed_integral<T>;
template<class T>
/*concept*/ constexpr bool swappable = Internal::swappable_impl<T>;
template<class T, class U>
/*concept*/ constexpr bool swappable_with = Internal::swappable_with_impl<T, U>;
}
namespace AZStd::Internal
{
// boolean-testable concept (exposition only in the C++standard)
template<class T>
constexpr bool boolean_testable_impl = convertible_to<T, bool>;
template<class T, class = void>
constexpr bool boolean_testable = false;
template<class T>
constexpr bool boolean_testable<T, enable_if_t<boolean_testable_impl<T> && boolean_testable_impl<decltype(!declval<T>())>>> = true;
// weakly comparable ==, !=
template<class T, class U, class = void>
constexpr bool weakly_equality_comparable_with = false;
template<class T, class U>
constexpr bool weakly_equality_comparable_with<T, U, enable_if_t<
boolean_testable<decltype(declval<AZStd::remove_reference_t<T>&>() == declval<AZStd::remove_reference_t<U>&>())>
&& boolean_testable<decltype(declval<AZStd::remove_reference_t<T>&>() != declval<AZStd::remove_reference_t<U>&>())>
&& boolean_testable<decltype(declval<AZStd::remove_reference_t<U>&>() == declval<AZStd::remove_reference_t<T>&>())>
&& boolean_testable<decltype(declval<AZStd::remove_reference_t<U>&>() != declval<AZStd::remove_reference_t<T>&>())>
>> = true;
// partially ordered <, >, <=, >=
template<class, class U, class = void>
constexpr bool partially_ordered_with_impl = false;
template<class T, class U>
constexpr bool partially_ordered_with_impl<T, U, enable_if_t<
boolean_testable<decltype(declval<const remove_reference_t<T>&>() < declval<const remove_reference_t<U>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<T>&>() > declval<const remove_reference_t<U>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<T>&>() <= declval<const remove_reference_t<U>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<T>&>() >= declval<const remove_reference_t<U>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<U>&>() < declval<const remove_reference_t<T>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<U>&>() > declval<const remove_reference_t<T>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<U>&>() <= declval<const remove_reference_t<T>&>())>
&& boolean_testable<decltype(declval<const remove_reference_t<U>&>() >= declval<const remove_reference_t<T>&>())>
>> = true;
}
namespace AZStd
{
template<class T>
/*concept*/ constexpr bool equality_comparable = Internal::weakly_equality_comparable_with<T, T>;
}
namespace AZStd::Internal
{
// equally_comparable + partially ordered
template<class, class U, class = void>
constexpr bool equally_comparable_with_impl = false;
template<class T, class U>
constexpr bool equally_comparable_with_impl<T, U, enable_if_t<equality_comparable<T>
&& equality_comparable<U>
&& common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&>
&& equality_comparable<common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>>
&& Internal::weakly_equality_comparable_with<T, U>
>> = true;
}
namespace AZStd
{
template<class T, class U>
/*concept*/ constexpr bool equality_comparable_with = Internal::equally_comparable_with_impl<T, U>;
template<class T, class U>
/*concept*/ constexpr bool partially_ordered_with = Internal::partially_ordered_with_impl<T, U>;
template<class T>
/*concept*/ constexpr bool totally_ordered = equality_comparable<T> && partially_ordered_with<T, T>;
}
namespace AZStd::Internal
{
// equally_comparable + partially ordered
template<class, class U, class = void>
constexpr bool totally_ordered_with_impl = false;
template<class T, class U>
constexpr bool totally_ordered_with_impl<T, U, enable_if_t<totally_ordered<T>&& totally_ordered<U>
&& equality_comparable_with<T, U>
&& totally_ordered<common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>>
&& partially_ordered_with<T, U>
>> = true;
}
namespace AZStd
{
template<class T, class U>
/*concept*/ constexpr bool totally_ordered_with = Internal::totally_ordered_with_impl<T, U>;
}
namespace AZStd::Internal
{
template<class T, class = void>
inline constexpr bool is_default_initializable = false;
template<class T>
inline constexpr bool is_default_initializable<T, void_t<decltype(::new T)>> = true;
template<class T, class = void>
constexpr bool default_initializable_impl = false;
template<class T>
constexpr bool default_initializable_impl < T, enable_if_t < constructible_from<T>
&& sfinae_trigger_v<decltype(T{}) > && Internal::is_default_initializable<T> >> = true;
template <class T, class = void>
constexpr bool movable_impl = false;
template <class T>
constexpr bool movable_impl<T, enable_if_t<is_object_v<T> && move_constructible<T> &&
assignable_from<T&, T> && swappable<T>> > = true;
template <class T, class = void>
constexpr bool copy_constructible_impl = false;
template <class T>
constexpr bool copy_constructible_impl<T, enable_if_t<move_constructible<T> &&
constructible_from<T, T&> && convertible_to<T&, T> &&
constructible_from<T, const T&> && convertible_to<const T&, T> &&
constructible_from<T, const T> && convertible_to<const T, T>> > = true;
}
namespace AZStd
{
// movable
template <class T>
/*concept*/ constexpr bool movable = Internal::movable_impl<T>;
// default_initializable
template<class T>
/*concept*/ constexpr bool default_initializable = Internal::default_initializable_impl<T>;
// copy constructible
template<class T>
/*concept*/ constexpr bool copy_constructible = Internal::copy_constructible_impl<T>;
}
namespace AZStd::Internal
{
template <class T, class = void>
constexpr bool copyable_impl = false;
template <class T>
constexpr bool copyable_impl<T, enable_if_t<copy_constructible<T> && movable<T> && assignable_from<T&, T&> &&
assignable_from<T&, const T&> && assignable_from<T&, const T>> > = true;
}
namespace AZStd
{
// copyable
template<class T>
/*concept*/ constexpr bool copyable = Internal::copyable_impl<T>;
// semiregular
template<class T>
/*concept*/ constexpr bool semiregular = copyable<T> && default_initializable<T>;
// regular
template<class T>
/*concept*/ constexpr bool regular = semiregular<T> && equality_comparable<T>;
}
// Iterator Concepts
namespace AZStd::Internal
{
template <class T>
constexpr bool is_integer_like = integral<T> && !same_as<T, bool>;
template <class T>
constexpr bool is_signed_integer_like = signed_integral<T>;
template <class T, class = void>
constexpr bool weakly_incrementable_impl = false;
template <class T>
constexpr bool weakly_incrementable_impl<T, enable_if_t<movable<T>
&& is_signed_integer_like<iter_difference_t<T>>
&& same_as<decltype(++declval<T&>()), T&>
&& sfinae_trigger_v<decltype(declval<T&>()++)> >> = true;
}
namespace AZStd
{
// models weakly_incrementable concept
template <class T>
/*concept*/ constexpr bool weakly_incrementable = Internal::weakly_incrementable_impl<T>;
// models input_or_output_iterator concept
template <class T>
/*concept*/ constexpr bool input_or_output_iterator = !is_void_v<T>
&& weakly_incrementable<T>;
}
namespace AZStd::Internal
{
template <class T, class = void>
constexpr bool incrementable_impl = false;
template <class T>
constexpr bool incrementable_impl<T, enable_if_t<regular<T>
&& weakly_incrementable<T>
&& same_as<decltype(declval<T&>()++), T> >> = true;
}
namespace AZStd
{
template <class T>
/*concept*/ constexpr bool incrementable = Internal::incrementable_impl<T>;
}
namespace AZStd
{
template<class S, class I>
/*concept*/ constexpr bool sentinel_for = semiregular<S> &&
input_or_output_iterator<I> &&
Internal::weakly_equality_comparable_with<S, I>;
template<class S, class I>
inline constexpr bool disable_sized_sentinel_for = false;
}
namespace AZStd::Internal
{
template<class S, class I, class = void>
/*concept*/ constexpr bool sized_sentinel_for_impl = false;
template<class S, class I>
/*concept*/ constexpr bool sized_sentinel_for_impl<S, I, enable_if_t<
sentinel_for<S, I>
&& !disable_sized_sentinel_for<remove_cv_t<S>, remove_cv_t<I>>
&& same_as<decltype(declval<S>() - declval<I>()), iter_difference_t<I>>
&& same_as<decltype(declval<I>() - declval<S>()), iter_difference_t<I>> >> = true;
}
namespace AZStd
{
template<class S, class I>
/*concept*/ constexpr bool sized_sentinel_for = Internal::sized_sentinel_for_impl<S, I>;
template<class I>
struct iterator_traits;
}
namespace AZStd::Internal
{
// ITER_CONCEPT(I) general concept
template<class I, class = void>
constexpr bool use_traits_iterator_concept_for_concept = false;
template<class I>
constexpr bool use_traits_iterator_concept_for_concept<I, void_t<typename iterator_traits<I>::iterator_concept>> = true;
template<class I, class = void>
constexpr bool use_traits_iterator_category_for_concept = false;
template<class I>
constexpr bool use_traits_iterator_category_for_concept<I,
void_t<typename iterator_traits<I>::iterator_category>> = !use_traits_iterator_concept_for_concept<I>;
template<class I, class = void>
constexpr bool use_random_access_iterator_tag_for_concept = false;
template<class I>
constexpr bool use_random_access_iterator_tag_for_concept<I,
void_t<iterator_traits<I>>> = !use_traits_iterator_concept_for_concept<I>
&& !use_traits_iterator_category_for_concept<I>;
template<class I, class = void>
struct iter_concept;
template<class I>
struct iter_concept<I, enable_if_t<use_traits_iterator_concept_for_concept<I>>>
{
using type = typename iterator_traits<I>::iterator_concept;
};
template<class I>
struct iter_concept<I, enable_if_t<use_traits_iterator_category_for_concept<I>>>
{
using type = typename iterator_traits<I>::iterator_category;
};
template<class I>
struct iter_concept<I, enable_if_t<use_random_access_iterator_tag_for_concept<I>>>
{
using type = random_access_iterator_tag;
};
template<class I>
using iter_concept_t = typename iter_concept<I>::type;
}
namespace AZStd
{
// indirectly readable
template <class In>
/*concept*/ constexpr bool indirectly_readable = Internal::indirectly_readable_impl<remove_cvref_t<In>>;
}
namespace AZStd::Internal
{
// model the indirectly writable concept
template <class Out, class T, class = void>
constexpr bool indirectly_writable_impl = false;
template <class Out, class T>
constexpr bool indirectly_writable_impl<Out, T, void_t<
decltype(*declval<Out&>() = declval<T>()),
decltype(*declval<Out>() = declval<T>()),
decltype(const_cast<const iter_reference_t<Out>&&>(*declval<Out&>()) = declval<T>()),
decltype(const_cast<const iter_reference_t<Out>&&>(*declval<Out>()) = declval<T>())>
> = true;
}
namespace AZStd
{
// indirectly writable
template <class Out, class T>
/*concept*/ constexpr bool indirectly_writable = Internal::indirectly_writable_impl<Out, T>;
// indirectly movable
template<class In, class Out>
/*concept*/ constexpr bool indirectly_movable = indirectly_readable<In> && indirectly_writable<Out, iter_rvalue_reference_t<In>>;
}
namespace AZStd::Internal
{
template<class In, class Out, class = void>
constexpr bool indirectly_movable_storage_impl = false;
template<class In, class Out>
constexpr bool indirectly_movable_storage_impl<In, Out, enable_if_t<
indirectly_movable<In, Out> &&
indirectly_writable<Out, iter_value_t<In>> &&
movable<iter_value_t<In>> &&
constructible_from<iter_value_t<In>, iter_rvalue_reference_t<In>> &&
assignable_from<iter_value_t<In>&, iter_rvalue_reference_t<In>>> > = true;
}
namespace AZStd
{
template<class In, class Out>
/*concept*/ constexpr bool indirectly_movable_storable = Internal::indirectly_movable_storage_impl<In, Out>;
}
namespace AZStd::Internal
{
template<class In, class Out, class = void>
constexpr bool indirectly_copyable_impl = false;
template<class In, class Out>
constexpr bool indirectly_copyable_impl<In, Out, enable_if_t<
indirectly_readable<In> &&
indirectly_writable<Out, iter_reference_t<In>>> > = true;
}
namespace AZStd
{
// indirectly copyable
template<class In, class Out>
/*concept*/ constexpr bool indirectly_copyable = Internal::indirectly_copyable_impl<In, Out>;
}
namespace AZStd::Internal
{
template<class In, class Out, class = void>
constexpr bool indirectly_copyable_storable_impl = false;
template<class In, class Out>
constexpr bool indirectly_copyable_storable_impl<In, Out, enable_if_t<
indirectly_copyable<In, Out> &&
indirectly_writable<Out, iter_value_t<In>&> &&
indirectly_writable<Out, const iter_value_t<In>&> &&
indirectly_writable<Out, iter_value_t<In>&&> &&
indirectly_writable<Out, const iter_value_t<In>&&> &&
copyable<iter_value_t<In>> &&
constructible_from<iter_value_t<In>, iter_reference_t<In>> &&
assignable_from<iter_value_t<In>&, iter_reference_t<In>>> > = true;
}
namespace AZStd
{
template<class In, class Out>
/*concept*/ constexpr bool indirectly_copyable_storable = Internal::indirectly_copyable_storable_impl<In, Out>;
}
namespace AZStd::ranges::Internal
{
template<class I1, class I2>
void iter_swap(I1, I2) = delete;
template <class I1, class I2, class = void>
constexpr bool iter_swap_adl = false;
template <class I1, class I2>
constexpr bool iter_swap_adl<I1, I2, void_t<decltype(iter_swap(declval<I1>(), declval<I2>()))>> = true;
template <class I1, class I2, class = void>
constexpr bool is_class_or_enum_with_iter_swap_adl = false;
template <class I1, class I2>
constexpr bool is_class_or_enum_with_iter_swap_adl<I1, I2, enable_if_t<iter_swap_adl<I1, I2>
&& (is_class_v<remove_cvref_t<I1>> || is_enum_v<remove_cvref_t<I1>>)
&& (is_class_v<remove_cvref_t<I2>> || is_enum_v<remove_cvref_t<I2>>)>> = true;
struct iter_swap_fn
{
template <class I1, class I2>
constexpr auto operator()(I1&& i1, I2&& i2) const
->enable_if_t<is_class_or_enum_with_iter_swap_adl<I1, I2>
>
{
iter_swap(AZStd::forward<I1>(i1), AZStd::forward<I1>(i2));
}
template <class I1, class I2>
constexpr auto operator()(I1&& i1, I2&& i2) const
->enable_if_t<!is_class_or_enum_with_iter_swap_adl<I1, I2>
&& indirectly_readable<I1>
&& indirectly_readable<I2>
&& swappable_with<iter_reference_t<I1>, iter_reference_t<I2>>
>
{
ranges::swap(*i1, *i2);
}
template <class I1, class I2>
constexpr auto operator()(I1&& i1, I2&& i2) const
->enable_if_t<!is_class_or_enum_with_iter_swap_adl<I1, I2>
&& indirectly_movable_storable<I1, I2>
&& indirectly_movable_storable<I2, I1>
>
{
*AZStd::forward<I1>(i1) = iter_exchange_move(AZStd::forward<I2>(i2), AZStd::forward<I1>(i1));
}
private:
template<class X, class Y>
static constexpr iter_value_t<X> iter_exchange_move(X&& x, Y&& y)
noexcept(noexcept(iter_value_t<X>(iter_move(x))) && noexcept(*x = iter_move(y)))
{
iter_value_t<X> old_value(iter_move(x));
*x = iter_move(y);
return old_value;
}
};
}
namespace AZStd::ranges
{
inline namespace customization_point_object
{
inline constexpr Internal::iter_swap_fn iter_swap{};
}
}
namespace AZStd::Internal
{
template <class I1, class I2, class = void>
constexpr bool indirectly_swappable_impl = false;
template <class I1, class I2>
constexpr bool indirectly_swappable_impl<I1, I2, enable_if_t<
indirectly_readable<I1>&& indirectly_readable<I2>
&& sfinae_trigger_v<
decltype(AZStd::ranges::iter_swap(declval<I1>(), declval<I1>())),
decltype(AZStd::ranges::iter_swap(declval<I2>(), declval<I2>())),
decltype(AZStd::ranges::iter_swap(declval<I1>(), declval<I2>())),
decltype(AZStd::ranges::iter_swap(declval<I2>(), declval<I1>()))>>> = true;
}
namespace AZStd
{
template<class I1, class I2 = I1>
/*concept*/ constexpr bool indirectly_swappable = Internal::indirectly_swappable_impl<I1, I2>;
}
namespace AZStd::Internal
{
template<class I, class = void>
constexpr bool input_iterator_impl = false;
template<class I>
constexpr bool input_iterator_impl<I, enable_if_t<input_or_output_iterator<I>
&& derived_from<iter_concept_t<I>, input_iterator_tag>
&& indirectly_readable<I>
>> = true;
}
namespace AZStd
{
// input iterator
template<class I>
/*concept*/ constexpr bool input_iterator = Internal::input_iterator_impl<I>;
}
namespace AZStd::Internal
{
template<class I, class T, class = void>
constexpr bool output_iterator_impl = false;
template<class I, class T>
constexpr bool output_iterator_impl<I, T, enable_if_t<input_or_output_iterator<I>
&& indirectly_writable<I, T>
&& sfinae_trigger_v<decltype(*declval<I&>()++ = AZStd::declval<T>())>
>> = true;
}
namespace AZStd
{
// output iterator
template<class I, class T>
/*concept*/ constexpr bool output_iterator = Internal::output_iterator_impl<I, T>;
}
namespace AZStd::Internal
{
template<class I, class = void>
constexpr bool forward_iterator_impl = false;
template<class I>
constexpr bool forward_iterator_impl<I, enable_if_t<input_iterator<I>
&& derived_from<Internal::iter_concept_t<I>, forward_iterator_tag>
&& incrementable<I>
&& sentinel_for<I, I>> > = true;
}
namespace AZStd
{
// forward_iterator
template<class I>
/*concept*/ constexpr bool forward_iterator = Internal::forward_iterator_impl<I>;
}
namespace AZStd::Internal
{
template<class I, class = void>
constexpr bool bidirectional_iterator_impl = false;
template<class I>
constexpr bool bidirectional_iterator_impl<I, enable_if_t<forward_iterator<I>
&& derived_from<iter_concept_t<I>, bidirectional_iterator_tag>
&& same_as<decltype(--declval<I&>()), I&>
&& same_as<decltype(declval<I&>()--), I> >> = true;
}
namespace AZStd
{
// bidirectional iterator
template<class I>
/*concept*/ constexpr bool bidirectional_iterator = Internal::bidirectional_iterator_impl<I>;
}
namespace AZStd::Internal
{
template<class I, class = void>
constexpr bool random_access_iterator_impl = false;
template<class I>
constexpr bool random_access_iterator_impl<I, enable_if_t<bidirectional_iterator<I>
&& derived_from<iter_concept_t<I>, random_access_iterator_tag>
&& totally_ordered<I>
&& sized_sentinel_for<I, I>
&& same_as<decltype(declval<I&>() += declval<const iter_difference_t<I>>()), I&>
&& same_as<decltype(declval<const I>() + declval<const iter_difference_t<I>>()), I>
&& same_as<decltype(declval<iter_difference_t<I>>() + declval<const I>()), I>
&& same_as<decltype(declval<I&>() -= declval<const iter_difference_t<I>>()), I&>
&& same_as<decltype(declval<const I>() - declval<const iter_difference_t<I>>()), I>
&& same_as<decltype(declval<const I&>()[declval<iter_difference_t<I>>()]), iter_reference_t<I>>>>
= true;
}
namespace AZStd
{
template<class I>
/*concept*/ constexpr bool random_access_iterator = Internal::random_access_iterator_impl<I>;
}
namespace AZStd::Internal
{
template<class I, class = void>
constexpr bool contiguous_iterator_impl = false;
template<class I>
constexpr bool contiguous_iterator_impl<I, enable_if_t<random_access_iterator<I>
&& derived_from<iter_concept_t<I>, contiguous_iterator_tag>
&& is_lvalue_reference_v<iter_reference_t<I>>
&& indirectly_readable<I>
&& same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>>
> >
= same_as<decltype(to_address(declval<const I&>())), add_pointer_t<iter_reference_t<I>>>;
}
namespace AZStd
{
// contiguous iterator
template<class I>
/*concept*/ constexpr bool contiguous_iterator = Internal::contiguous_iterator_impl<I>;
}
namespace AZStd::Internal
{
// models the predicate concept
template <bool, class F, class... Args>
constexpr bool predicate_impl = false;
template <class F, class... Args>
constexpr bool predicate_impl<true, F, Args...> = Internal::boolean_testable<invoke_result_t<F, Args...>>;
}
namespace AZStd
{
// models the predicate concept
template <class F, class... Args>
/*concept*/ constexpr bool predicate = Internal::predicate_impl<regular_invocable<F, Args...>, F, Args...>;
// models the relation concept
template <class R, class T, class U>
/*concept*/ constexpr bool relation = predicate<R, T, T> && predicate<R, U, U>
&& predicate<R, T, U> && predicate<R, U, T>;
// models the equivalence_relation concept
template <class R, class T, class U>
/*concept*/ constexpr bool equivalence_relation = relation<R, T, U>;
// models the strict_weak_order concept
// Note: semantically this is different than equivalence_relation
template <class R, class T, class U>
/*concept*/ constexpr bool strict_weak_order = relation<R, T, U>;
}
@@ -135,9 +135,10 @@ namespace AZStd
AZ_FORCE_INLINE this_type& operator--() { --m_offset; return *this; }
AZ_FORCE_INLINE this_type operator--(int) { this_type tmp = *this; --m_offset; return tmp; }
AZ_FORCE_INLINE this_type& operator+=(difference_type offset) { m_offset += offset; return *this; }
AZ_FORCE_INLINE this_type operator+(difference_type offset) { this_type tmp = *this; tmp += offset; return tmp; }
AZ_FORCE_INLINE this_type operator+(difference_type offset) const { this_type tmp = *this; tmp += offset; return tmp; }
friend AZ_FORCE_INLINE this_type operator+(difference_type offset, const this_type& rhs) { this_type tmp = rhs; tmp += offset; return tmp; }
AZ_FORCE_INLINE this_type& operator-=(difference_type offset) { m_offset -= offset; return *this; }
AZ_FORCE_INLINE this_type operator-(difference_type offset) { this_type tmp = *this; tmp -= offset; return tmp; }
AZ_FORCE_INLINE this_type operator-(difference_type offset) const { this_type tmp = *this; tmp -= offset; return tmp; }
/// ???
AZ_FORCE_INLINE difference_type operator-(const this_type& rhs) const
{
@@ -197,9 +198,10 @@ namespace AZStd
AZ_FORCE_INLINE this_type& operator--() { --base_type::m_offset; return *this; }
AZ_FORCE_INLINE this_type operator--(int) { this_type tmp = *this; --base_type::m_offset; return tmp; }
AZ_FORCE_INLINE this_type& operator+=(difference_type offset) { base_type::m_offset += offset; return *this; }
AZ_FORCE_INLINE this_type operator+(difference_type offset) { this_type tmp = *this; tmp += offset; return tmp; }
AZ_FORCE_INLINE this_type operator+(difference_type offset) const { this_type tmp = *this; tmp += offset; return tmp; }
friend AZ_FORCE_INLINE this_type operator+(difference_type offset, const this_type& rhs) { this_type tmp = rhs; tmp += offset; return tmp; }
AZ_FORCE_INLINE this_type& operator-=(difference_type offset) { base_type::m_offset -= offset; return *this; }
AZ_FORCE_INLINE this_type operator-(difference_type offset) { this_type tmp = *this; tmp -= offset; return tmp; }
AZ_FORCE_INLINE this_type operator-(difference_type offset) const { this_type tmp = *this; tmp -= offset; return tmp; }
AZ_FORCE_INLINE difference_type operator-(const this_type& rhs) const
{
return rhs.m_offset <= base_type::m_offset ? base_type::m_offset - rhs.m_offset : -(difference_type)(rhs.m_offset - base_type::m_offset);
@@ -9,6 +9,7 @@
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/concepts/concepts.h>
#include <AzCore/std/createdestroy.h>
#include <AzCore/std/typetraits/typetraits.h>
@@ -101,7 +102,7 @@ namespace AZStd::Internal
//! Invokes destructor on all elements in range
//! No-op on empty container
//! Nothing to destroy since the storage is empty.
template <typename InputIt, typename = enable_if_t<Internal::is_input_iterator_v<InputIt>>>
template <typename InputIt, typename = enable_if_t<input_iterator<InputIt>>>
static constexpr void unsafe_destroy(InputIt, InputIt) noexcept
{
}
@@ -214,7 +215,7 @@ namespace AZStd::Internal
//! Destructs elements in the range [begin, end).
//! This does not modify the size of the storage
//! This is a no-op for trivial types
template <typename InputIt, typename = enable_if_t<Internal::is_input_iterator_v<InputIt>>>
template <typename InputIt, typename = enable_if_t<input_iterator<InputIt>>>
void unsafe_destroy(InputIt, InputIt) noexcept
{
}
@@ -334,7 +335,7 @@ namespace AZStd::Internal
//! Destructs elements in the range [begin, end).
//! This does not modify the size of the storage
//! Invokes the destuctor via the AZStd::destroy method
template <typename InputIt, typename = enable_if_t<Internal::is_input_iterator_v<InputIt>>>
template <typename InputIt, typename = enable_if_t<input_iterator<InputIt>>>
void unsafe_destroy(InputIt first, InputIt last) noexcept(is_nothrow_destructible_v<value_type>)
{
AZSTD_CONTAINER_ASSERT(first >= data() && first <= data() + size(), "begin iterator is not in range of storage");
@@ -410,7 +411,7 @@ namespace AZStd
AZStd::uninitialized_fill_n(data(), numElements, value);
}
template <class InputIt, typename = AZStd::enable_if_t<Internal::is_input_iterator_v<InputIt>>>
template <class InputIt, typename = AZStd::enable_if_t<input_iterator<InputIt>>>
fixed_vector(InputIt first, InputIt last)
{
resize_no_construct(AZStd::distance(first, last));
@@ -615,7 +616,7 @@ namespace AZStd
insert(end(), numElements, value);
}
template <class InputIt, typename = AZStd::enable_if_t<Internal::is_input_iterator_v<InputIt>>>
template <class InputIt, typename = AZStd::enable_if_t<input_iterator<InputIt>>>
void assign(InputIt first, InputIt last)
{
clear();
@@ -641,8 +642,18 @@ namespace AZStd
return &newElement;
}
AZStd::uninitialized_move(insertPosPtr, dataEnd, insertPosPtr + 1);
// We need to move data with care, it is overlapping.
// first move the last element into the uninitialized position as that will not overlap.
pointer nonOverlap = dataEnd - 1;
AZStd::uninitialized_move(nonOverlap, dataEnd, dataEnd);
// copy the memory backwards while performing AZStd::move on the existing elements the area with overlapping memory
// to move the elments to the right by 1
AZStd::move_backward(insertPosPtr, nonOverlap, dataEnd);
// add new elements
AZStd::construct_at(insertPosPtr, AZStd::forward<Args>(args)...);
resize_no_construct(size() + 1);
return iterator(insertPosPtr);
}
iterator insert(const_iterator insertPos, const_reference value)
@@ -707,7 +718,7 @@ namespace AZStd
}
}
template<class InputIt, typename = AZStd::enable_if_t<Internal::is_input_iterator_v<InputIt>>>
template<class InputIt, typename = AZStd::enable_if_t<input_iterator<InputIt>>>
void insert(const_iterator insertPos, InputIt first, InputIt last)
{
// specialize for iterator categories.
@@ -7,19 +7,46 @@
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/limits.h>
#include <AzCore/std/ranges/ranges.h>
#include <AzCore/std/typetraits/type_identity.h>
namespace AZStd
{
inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
template <class T, size_t Extent = dynamic_extent>
class span;
}
namespace AZStd::Internal
{
template <class T>
inline constexpr bool is_std_array = false;
template <class U, size_t N>
inline constexpr bool is_std_array<::AZStd::array<U, N>> = true;
template <class T>
inline constexpr bool is_std_span = false;
template <class U, size_t Extent>
inline constexpr bool is_std_span<::AZStd::span<U, Extent>> = true;
template <class T, class U>
inline constexpr bool is_array_convertible = is_convertible_v<T(*)[], U(*)[]>;
}
namespace AZStd
{
/**
* First pass partial implementation of span copied over from array_view. It
* returns non-const iterator/pointers. first(), last(), and subspan()
* are yet to be implemented. It does not maintain storage for the data,
* but just holds pointers to mark the beginning and end of the array.
* It can be conveniently constructed from a variety of other container
* types like array, vector, and fixed_vector.
* Full C++20 implementation of span done using the C++ draft at https://eel.is/c++draft/views.
* It does not maintain storage for the data,
* but just hold a pointer to mark the beginning and the size for the elements.
* It can be constructed any type that models the C++ contiguous_range concept
* such like array, vector, fixed_vector, raw-array, string_view, string, etc... .
*
* Example:
* Given "void Func(AZStd::span<int> a) {...}" you can call...
@@ -33,97 +60,149 @@ namespace AZStd
*
* Since the span does not copy and store any data, it is only valid as long as the data used to create it is valid.
*/
template <class T>
class span final
template <class T, size_t Extent>
class span
{
public:
using element_type = T;
using value_type = AZStd::remove_cv_t<T>;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = AZStd::size_t;
using difference_type = AZStd::ptrdiff_t;
using iterator = T*;
using const_iterator = const T*;
using pointer = element_type*;
using const_pointer = const element_type*;
using reference = element_type&;
using const_reference = const element_type&;
using iterator = element_type*;
using const_iterator = const element_type*;
using reverse_iterator = AZStd::reverse_iterator<iterator>;
using const_reverse_iterator = AZStd::reverse_iterator<const_iterator>;
constexpr span();
inline static constexpr size_t extent = Extent;
constexpr span() noexcept = default;
~span() = default;
constexpr span(pointer s, size_type length);
template <class It, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
Extent == dynamic_extent>* = nullptr>
constexpr span(It first, size_type length);
constexpr span(pointer first, pointer last);
template <class It, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
Extent != dynamic_extent, int> = 0>
constexpr explicit span(It first, size_type length);
// We explicitly delete this constructor because it's too easy to accidentally
// create a span to just the first element instead of an entire array.
constexpr span(const_pointer s) = delete;
template <class It, class End, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
sized_sentinel_for<End, It> &&
Extent == dynamic_extent>* = nullptr>
constexpr span(It first, End last);
template<typename Container>
constexpr span(Container& data);
template <class It, class End, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
sized_sentinel_for<End, It> &&
Extent != dynamic_extent, int> = 0>
constexpr explicit span(It first, End last);
template<typename Container>
constexpr span(const Container& data);
template<size_t N, class = enable_if_t<extent == dynamic_extent || N == Extent>>
constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept;
constexpr span(const span&) = default;
template <class U, size_t N, class = enable_if_t<extent == dynamic_extent || N == Extent>>
constexpr span(array<U, N>& data) noexcept;
template <class U, size_t N, class = enable_if_t<extent == dynamic_extent || N == Extent>>
constexpr span(const array<U, N>& data) noexcept;
constexpr span(span&& other);
template <class R, class = enable_if_t<ranges::contiguous_range<R> &&
ranges::sized_range<R> &&
(ranges::borrowed_range<R> || is_const_v<element_type>) &&
!Internal::is_std_span<remove_cvref_t<R>> &&
!Internal::is_std_array<remove_cvref_t<R>> &&
!is_array_v<remove_cvref_t<R>> &&
Internal::is_array_convertible<remove_reference_t<ranges::range_reference_t<R>>, element_type> >>
constexpr span(R&& r);
template <class U, size_t OtherExtent, class = enable_if_t<
(extent == dynamic_extent || OtherExtent == dynamic_extent || extent == OtherExtent)
&& Internal::is_array_convertible<U, element_type> >>
constexpr span(const span<U, OtherExtent>& other);
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span& other) = default;
constexpr span& operator=(span&& other);
// subviews -> https://eel.is/c++draft/views#span.sub
template <size_t Count>
constexpr span<element_type, Count> first() const;
template <size_t Count>
constexpr span<element_type, Count> last() const;
template <size_t Offset, size_t Count = dynamic_extent>
constexpr auto subspan() const;
constexpr size_type size() const;
constexpr span<element_type, dynamic_extent> first(size_type count) const;
constexpr span<element_type, dynamic_extent> last(size_type count) const;
constexpr span<element_type, dynamic_extent> subspan(size_type offset, size_type count = dynamic_extent) const;
constexpr bool empty() const;
// observers - https://eel.is/c++draft/views#span.obs
constexpr size_type size() const noexcept;
constexpr size_type size_bytes() const noexcept;
constexpr pointer data();
constexpr const_pointer data() const;
[[nodiscard]] constexpr bool empty() const noexcept;
constexpr const_reference operator[](size_type index) const;
constexpr reference operator[](size_type index);
// element access - https://eel.is/c++draft/views#span.elem
constexpr reference operator[](size_type index) const;
constexpr reference front() const;
constexpr reference back() const;
constexpr pointer data() const noexcept;
constexpr void erase();
// iterator support - https://eel.is/c++draft/views#span.iterators
constexpr iterator begin() const noexcept;
constexpr iterator end() const noexcept;
constexpr iterator begin();
constexpr iterator end();
constexpr const_iterator begin() const;
constexpr const_iterator end() const;
constexpr const_iterator cbegin() const;
constexpr const_iterator cend() const;
constexpr reverse_iterator rbegin();
constexpr reverse_iterator rend();
constexpr const_reverse_iterator rbegin() const;
constexpr const_reverse_iterator rend() const;
constexpr const_reverse_iterator crbegin() const;
constexpr const_reverse_iterator crend() const;
friend bool operator==(span lhs, span rhs)
{
return lhs.m_begin == rhs.m_begin && lhs.m_end == rhs.m_end;
}
friend bool operator!=(span lhs, span rhs) { return !(lhs == rhs); }
friend bool operator< (span lhs, span rhs) { return lhs.m_begin < rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end < rhs.m_end; }
friend bool operator> (span lhs, span rhs) { return lhs.m_begin > rhs.m_begin || lhs.m_begin == rhs.m_begin && lhs.m_end > rhs.m_end; }
friend bool operator<=(span lhs, span rhs) { return lhs == rhs || lhs < rhs; }
friend bool operator>=(span lhs, span rhs) { return lhs == rhs || lhs > rhs; }
constexpr reverse_iterator rbegin() const noexcept;
constexpr reverse_iterator rend() const noexcept;
private:
pointer m_begin;
pointer m_end;
pointer m_data{};
size_type m_size{};
};
// deduction guides https://eel.is/c++draft/views#span.deduct
template <class It, class EndOrSize, class = enable_if_t<contiguous_iterator<It>>>
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>>;
// array deductions
template <class T, size_t N>
span(T(&)[N]) -> span<T, N>;
template <class T, size_t N>
span(array<T, N>&) -> span<T, N>;
template <class T, size_t N>
span(const array<T, N>&) -> span<const T, N>;
template <class R, class = enable_if_t<ranges::contiguous_range<R>>>
span(R&&) -> span<remove_reference_t<ranges::range_reference_t<R>>>;
// [span.objectrep], views of object representation
template <class ElementType, size_t Extent>
auto as_bytes(span<ElementType, Extent> s) noexcept
-> span<const byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>;
template <class ElementType, size_t Extent>
auto as_writable_bytes(span<ElementType, Extent> s) noexcept
-> enable_if_t<!is_const_v<ElementType>, span<byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>>;
} // namespace AZStd
namespace AZStd::ranges
{
template<class ElementType, size_t Extent>
inline constexpr bool enable_view<span<ElementType, Extent>> = true;
template<class ElementType, size_t Extent>
inline constexpr bool enable_borrowed_range<span<ElementType, Extent>> = true;
}
#include <AzCore/std/containers/span.inl>
@@ -9,116 +9,206 @@
namespace AZStd
{
template <class Element>
inline constexpr span<Element>::span()
: m_begin(nullptr)
, m_end(nullptr)
{ }
template <class T, size_t Extent>
template <class It, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
Extent == dynamic_extent>*>
inline constexpr span<T, Extent>::span(It first, size_type length)
: m_data{ to_address(first) }
, m_size{ length }
{}
template <class Element>
inline constexpr span<Element>::span(pointer s, size_type length)
: m_begin(s)
, m_end(m_begin + length)
template <class T, size_t Extent>
template <class It, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
Extent != dynamic_extent, int>>
inline constexpr span<T, Extent>::span(It first, size_type length)
: m_data{ to_address(first) }
, m_size{ length }
{}
template <class T, size_t Extent>
template <class It, class End, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
sized_sentinel_for<End, It> &&
Extent == dynamic_extent>*>
inline constexpr span<T, Extent>::span(It first, End last)
: m_data{to_address(first)}
, m_size(last - first)
{}
template <class T, size_t Extent>
template <class It, class End, enable_if_t<contiguous_iterator<It> &&
Internal::is_array_convertible<remove_reference_t<iter_reference_t<It>>, T> &&
sized_sentinel_for<End, It> &&
Extent != dynamic_extent, int>>
inline constexpr span<T, Extent>::span(It first, End last)
: m_data{to_address(first)}
, m_size(last - first)
{}
template <class T, size_t Extent>
template <size_t N, class>
inline constexpr span<T, Extent>::span(type_identity_t<element_type>(&arr)[N]) noexcept
: m_data{ arr }
, m_size{ N }
{}
template <class T, size_t Extent>
template <class U, size_t N, class>
inline constexpr span<T, Extent>::span(array<U, N>& arr) noexcept
: m_data{ arr.data() }
, m_size{ arr.size() }
{}
template <class T, size_t Extent>
template <class U, size_t N, class>
inline constexpr span<T, Extent>::span(const array<U, N>& arr) noexcept
: m_data{ arr.data() }
, m_size{ arr.size() }
{}
template <class T, size_t Extent>
template <class R, class>
inline constexpr span<T, Extent>::span(R&& r)
: m_data{ ranges::data(r) }
, m_size{ ranges::size(r) }
{
if (length == 0) erase();
AZ_Assert(Extent == dynamic_extent || Extent == m_size, "The extent of the span is non dynamic,"
" therefore the range size must match the extent. Extent=%zu, Range size=%zu",
Extent, ranges::size(r));
}
template <class Element>
inline constexpr span<Element>::span(pointer first, pointer last)
: m_begin(first)
, m_end(last)
{ }
template<class Element>
template<typename Container>
inline constexpr span<Element>::span(Container& data)
: m_begin(data.data())
, m_end(m_begin + data.size())
{ }
template<class Element>
template<typename Container>
inline constexpr span<Element>::span(const Container& data)
: m_begin(data.data())
, m_end(m_begin + data.size())
{ }
template <class Element>
inline constexpr span<Element>::span(span&& other)
: span(other.m_begin, other.m_end)
template <class T, size_t Extent>
template <class U, size_t OtherExtent, class>
inline constexpr span<T, Extent>::span(const span<U, OtherExtent>& other)
: m_data{ other.data() }
, m_size{ other.size() }
{
#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging
other.m_begin = nullptr;
other.m_end = nullptr;
#endif
AZ_Assert(Extent == dynamic_extent || Extent == m_size, "The extent of the span is non dynamic,"
" therefore the current size of the other span must match the extent. Extent=%zu, Other span size=%zu",
Extent, other.size());
}
template <class Element>
inline constexpr AZStd::size_t span<Element>::size() const { return m_end - m_begin; }
template <class Element>
inline constexpr bool span<Element>::empty() const { return m_end == m_begin; }
template <class Element>
inline constexpr Element* span<Element>::data() { return m_begin; }
template <class Element>
inline constexpr const Element* span<Element>::data() const { return m_begin; }
template <class Element>
inline constexpr span<Element>& span<Element>::operator=(span<Element>&& other)
// subviews
template <class T, size_t Extent>
template <size_t Count>
inline constexpr auto span<T, Extent>::first() const -> span<element_type, Count>
{
m_begin = other.m_begin;
m_end = other.m_end;
#if AZ_DEBUG_BUILD // Clearing the original pointers isn't necessary, but is good for debugging
other.m_begin = nullptr;
other.m_end = nullptr;
#endif
return *this;
static_assert(Count <= Extent, "Count is larger than the Extent of the span, a subview of the first"
" Count elemnts of the span cannot be returned");
AZ_Assert(Count <= size(), "Count %zu is larger than span size %zu", Count, size());
return span<element_type, Count>{data(), Count};
}
template <class Element>
inline constexpr const Element& span<Element>::operator[](AZStd::size_t index) const
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::first(size_type count) const -> span<element_type, dynamic_extent>
{
AZ_Assert(count <= size(), "Count %zu is larger than current size of span size %zu", count, size());
return { data(), count };
}
template <class T, size_t Extent>
template <size_t Count>
inline constexpr auto span<T, Extent>::last() const -> span<element_type, Count>
{
static_assert(Count <= Extent, "Count is larger than the Extent of the span, a subview of the last"
" Count elements of the span cannot be returned");
AZ_Assert(Count <= size(), "Count %zu is larger than span size %zu", Count, size());
return span<element_type, Count>{data() + (size() - Count), Count};
}
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::last(size_type count) const -> span<element_type, dynamic_extent>
{
AZ_Assert(count <= size(), "Count %zu is larger than span size %zu", count, size());
return { data() + (size() - count), count };
}
template <class T, size_t Extent>
template <size_t Offset, size_t Count>
inline constexpr auto span<T, Extent>::subspan() const
{
static_assert(Offset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset),
"Subspan Offset must <= span Extent and the Count must be either dynamic_extent"
" or <= (span Extent - Offset)");
AZ_Assert(Offset <= size() && (Count == dynamic_extent || Count <= size() - Offset),
"Either the Subspan Offset %zu is larger than the span size %zu or the Count != dynamic_extent and"
" its value %zu is greater than \"span size - Offset\" %zu",
Offset, size(), Count, size() - Offset);
using return_type = span<element_type, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)>;
return return_type{ data() + Offset, Count != dynamic_extent ? Count : size() - Offset };
}
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::subspan(size_type offset, size_type count) const -> span<element_type, dynamic_extent>
{
AZ_Assert(offset <= size() && (count == dynamic_extent || count <= size() - offset),
"Either the Subspan offset %zu is larger than the span size %zu or the count != dynamic_extent and"
" its value %zu is greater than \"span size - offset\" %zu",
offset, size(), count, size() - offset);
return { data() + offset, count != dynamic_extent ? count : size() - offset };
}
// observers
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::size() const noexcept -> size_type { return m_size; }
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::size_bytes() const noexcept -> size_type { return m_size * sizeof(element_type); }
template <class T, size_t Extent>
[[nodiscard]] inline constexpr bool span<T, Extent>::empty() const noexcept{ return size() == 0; }
// element access
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::operator[](size_type index) const -> reference
{
AZ_Assert(index < size(), "index value is out of range");
return m_begin[index];
return data()[index];
}
template <class Element>
inline constexpr Element& span<Element>::operator[](AZStd::size_t index)
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::front() const -> reference
{
AZ_Assert(index < size(), "index value is out of range");
return m_begin[index];
AZ_Assert(!empty(), "span cannot be empty when invoking front");
return *data();
}
template <class Element>
inline constexpr void span<Element>::erase() { m_begin = m_end = nullptr; }
template <class Element>
inline constexpr Element* span<Element>::begin() { return m_begin; }
template <class Element>
inline constexpr Element* span<Element>::end() { return m_end; }
template <class Element>
inline constexpr const Element* span<Element>::begin() const { return m_begin; }
template <class Element>
inline constexpr const Element* span<Element>::end() const { return m_end; }
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::back() const -> reference
{
AZ_Assert(!empty(), "span cannot be empty when invoking back");
return *(data() + (size() - 1));
}
template <class Element>
inline constexpr const Element* span<Element>::cbegin() const { return m_begin; }
template <class Element>
inline constexpr const Element* span<Element>::cend() const { return m_end; }
// iterator support
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::data() const noexcept -> pointer { return m_data; }
template <class Element>
inline constexpr AZStd::reverse_iterator<Element*> span<Element>::rbegin() { return AZStd::reverse_iterator<Element*>(m_end); }
template <class Element>
inline constexpr AZStd::reverse_iterator<Element*> span<Element>::rend() { return AZStd::reverse_iterator<Element*>(m_begin); }
template <class Element>
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::rbegin() const { return AZStd::reverse_iterator<const Element*>(m_end); }
template <class Element>
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::rend() const { return AZStd::reverse_iterator<const Element*>(m_begin); }
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::begin() const noexcept -> iterator{ return m_data; }
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::end() const noexcept -> iterator { return m_data + m_size; }
template <class Element>
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::crbegin() const { return AZStd::reverse_iterator<const Element*>(cend()); }
template <class Element>
inline constexpr AZStd::reverse_iterator<const Element*> span<Element>::crend() const { return AZStd::reverse_iterator<const Element*>(cbegin()); }
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::rbegin() const noexcept -> reverse_iterator { return AZStd::make_reverse_iterator(end()); }
template <class T, size_t Extent>
inline constexpr auto span<T, Extent>::rend() const noexcept -> reverse_iterator { return AZStd::make_reverse_iterator(begin()); }
template <class ElementType, size_t Extent>
inline auto as_bytes(span<ElementType, Extent> s) noexcept
-> span<const byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
{
return span<const byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>(
reinterpret_cast<const byte*>(s.data()), s.size_bytes());
}
template <class ElementType, size_t Extent>
inline auto as_writable_bytes(span<ElementType, Extent> s) noexcept
-> enable_if_t<!is_const_v<ElementType>, span<byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>>
{
return span<byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>(
reinterpret_cast<byte*>(s.data()), s.size_bytes());
}
} // namespace AZStd
+357 -277
View File
@@ -7,22 +7,19 @@
*/
#pragma once
#include <AzCore/std/concepts/concepts.h>
#include <AzCore/std/iterator.h>
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/std/typetraits/is_array.h>
#include <AzCore/std/typetraits/is_assignable.h>
#include <AzCore/std/typetraits/is_constructible.h>
#include <AzCore/std/typetraits/is_destructible.h>
#include <AzCore/std/typetraits/is_function.h>
#include <AzCore/std/typetraits/is_trivially_copyable.h>
#include <AzCore/std/typetraits/is_void.h>
#include <AzCore/std/utils.h> // AZStd::addressof
namespace AZStd
{
// alias std::pointer_traits into the AZStd::namespace
using std::pointer_traits;
//! Bring the names of uninitialized_default_construct and
//! uninitialized_default_construct_n into the AZStd namespace
using std::uninitialized_default_construct;
@@ -34,42 +31,6 @@ namespace AZStd
using std::uninitialized_value_construct_n;
}
namespace AZStd::Internal
{
template <typename T, typename = void>
constexpr bool pointer_traits_has_to_address_v = false;
template <typename T>
constexpr bool pointer_traits_has_to_address_v<T, AZStd::void_t<decltype(AZStd::pointer_traits<T>::to_address(declval<const T&>()))>> = true;
}
namespace AZStd
{
//! Implements the C++20 to_address function
//! This obtains the address represented by ptr without forming a reference
//! to the pointee type
template <typename T>
constexpr T* to_address(T* ptr) noexcept
{
static_assert(!AZStd::is_function_v<T>, "Invoking to address on a function pointer is not allowed");
return ptr;
}
//! Fancy pointer overload which delegates to using a specialization of pointer_traits<T>::to_address
//! if that is a well-formed expression, otherwise it returns ptr->operator->()
//! For example invoking `to_address(AZStd::reverse_iterator<const char*>(char_ptr))`
//! Returns an element of type const char*
template <typename T>
constexpr auto to_address(const T& ptr) noexcept
{
if constexpr (AZStd::Internal::pointer_traits_has_to_address_v<T>)
{
return pointer_traits<T>::to_address(ptr);
}
else
{
return AZStd::to_address(ptr.operator->());
}
}
}
namespace AZStd::Internal
{
/**
@@ -81,7 +42,7 @@ namespace AZStd::Internal
/**
* Type has trivial destructor. We don't call it.
*/
template <class InputIterator, class ValueType = typename iterator_traits<InputIterator>::value_type, bool = is_trivially_destructible_v<ValueType>>
template <class InputIterator, class ValueType = iter_value_t<InputIterator>, bool = is_trivially_destructible_v<ValueType>>
struct destroy
{
static constexpr void range(InputIterator first, InputIterator last) { (void)first; (void)last; }
@@ -163,7 +124,7 @@ namespace AZStd::Internal
* Default object construction.
*/
// placement new isn't a core constant expression therefore it cannot be used in a constexpr function
template<class InputIterator, class ValueType = typename iterator_traits<InputIterator>::value_type,
template<class InputIterator, class ValueType = iter_value_t<InputIterator>,
bool = is_trivially_constructible_v<ValueType>>
struct construct
{
@@ -242,93 +203,125 @@ namespace AZStd::Internal
//////////////////////////////////////////////////////////////////////////
// Sequence copy. If we use optimized version we use memcpy.
/**
* Helper class to determine if we have apply fast copy. There are 2 conditions
* Class to determine if we have apply fast copy. There are 2 conditions
* - trivial copy ctor.
* - all iterators satisfy the C++20 are contiguous iterator concept: pointers or iterator classes with
* the iterator_concept typedef set to contiguous_iterator_tag
* - all iterators satisfy the C++20 are contiguous iterator concept
*/
template<class Out, class = void>
constexpr bool indirectly_trivially_copyable = false;
template<class Out>
constexpr bool indirectly_trivially_copyable<Out,
enable_if_t<indirectly_readable<Out>>> = is_trivially_copyable_v<iter_value_t<Out>>;
template<class InputIterator, class ResultIterator>
struct is_fast_copy_helper
{
using value_type = typename iterator_traits<ResultIterator>::value_type;
static constexpr bool value = AZStd::is_trivially_copyable_v<value_type>
&& Internal::satisfies_contiguous_iterator_concept_v<InputIterator>
&& Internal::satisfies_contiguous_iterator_concept_v<ResultIterator>;
};
using is_fast_copy = bool_constant<indirectly_trivially_copyable<ResultIterator>
&& contiguous_iterator<InputIterator>
&& contiguous_iterator<ResultIterator>
>;
// Use this trait to to determine copy mode, based on the iterator category and object copy properties,
// Use it when when you call uninitialized_copy, Internal::copy, Internal::move, etc.
template< typename InputIterator, typename ResultIterator >
struct is_fast_copy
: public ::AZStd::integral_constant<bool, ::AZStd::Internal::is_fast_copy_helper<InputIterator, ResultIterator>::value> {};
template<class InputIterator, class ResultIterator>
constexpr bool is_fast_copy_v = is_fast_copy<InputIterator, ResultIterator>::value;
// is_fast_copy argument is no longer used.
template <class InputIterator, class ForwardIterator>
constexpr ForwardIterator copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
constexpr ForwardIterator copy(InputIterator first, InputIterator last, ForwardIterator result, bool)
{
InputIterator iter(first);
for (; iter != last; ++result, ++iter)
if constexpr (is_fast_copy_v<InputIterator, ForwardIterator>)
{
*result = *iter;
}
// Specialized copy for contiguous iterators which are trivially copyable
size_t numElements = last - first;
if (numElements > 0)
{
#if az_has_builtin_memcpy
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Size of value types must match for a trivial copy");
__builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
#else
if (az_builtin_is_constant_evaluated())
{
for (; first != last; ++result, ++first)
{
*result = *first;
}
return result;
return result;
}
else
{
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Size of value types must match for a trivial copy");
AZ_Assert((static_cast<const void*>(&*result) < static_cast<const void*>(&*first))
|| (static_cast<const void*>(&*result) >= static_cast<const void*>(&*first + numElements)),
"AZStd::copy memory overlaps use AZStd::copy_backward!");
::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
}
#endif
}
return result + numElements;
}
else
{
for (; first != last; ++result, ++first)
{
*result = *first;
}
return result;
}
}
// Specialized copy for contiguous iterators (pointers) and trivial copy type.
// This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers
template <class InputIterator, class ForwardIterator>
inline ForwardIterator copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
{
// \todo Make sure memory ranges don't overlap, otherwise people should use move and move_backward.
static_assert(sizeof(typename iterator_traits<InputIterator>::value_type) == sizeof(typename iterator_traits<ForwardIterator>::value_type), "Size of value types must match for a trivial copy");
AZStd::size_t numElements = last - first;
if (numElements > 0)
{
AZ_Assert((static_cast<const void*>(&*result) < static_cast<const void*>(&*first)) || (static_cast<const void*>(&*result) >= static_cast<const void*>(&*first + numElements)), "AZStd::copy memory overlaps use AZStd::copy_backward!");
AZ_Assert((static_cast<const void*>(&*result + numElements) <= static_cast<const void*>(&*first)) || (static_cast<const void*>(&*result + numElements) > static_cast<const void*>(&*first + numElements)), "AZStd::copy memory overlaps use AZStd::copy_backward!");
/*AZSTD_STL::*/ memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits<InputIterator>::value_type));
}
return result + numElements;
}
// Copy backward.
template <class BidirectionalIterator1, class BidirectionalIterator2>
constexpr BidirectionalIterator2 copy_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const false_type& /* is_fast_copy<BidirectionalIterator1,BidirectionalIterator2>() */)
constexpr BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result, bool)
{
BidirectionalIterator1 iter(last);
while (first != iter)
if constexpr (is_fast_copy_v<BidirectionalIterator1, BidirectionalIterator2>)
{
*--result = *--iter;
// Specialized copy for contiguous iterators which are trivially copyable
size_t numElements = last - first;
if (numElements > 0)
{
#if az_has_builtin_memmove
static_assert(sizeof(iter_value_t<BidirectionalIterator1>) == sizeof(iter_value_t<BidirectionalIterator2>), "Size of value types must match for a trivial copy");
result -= numElements;
__builtin_memmove(to_address(result), to_address(first), numElements * sizeof(iter_value_t<BidirectionalIterator1>));
#else
if (az_builtin_is_constant_evaluated())
{
while (first != last)
{
*--result = *--last;
}
return result;
}
else
{
static_assert(sizeof(iter_value_t<BidirectionalIterator1>) == sizeof(iter_value_t<BidirectionalIterator2>), "Size of value types must match for a trivial copy");
result -= numElements;
AZ_Assert(((&*result + numElements) <= &*first) || ((&*result + numElements) > (&*first + numElements)), "AZStd::copy_backward memory overlaps use AZStd::copy!");
::memmove(&*result, &*first, numElements * sizeof(iter_value_t<BidirectionalIterator1>));
}
#endif
}
return result;
}
return result;
}
// Specialized copy for contiguous iterators (pointers) and trivial copy type.
// This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers
template <class BidirectionalIterator1, class BidirectionalIterator2>
inline BidirectionalIterator2 copy_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const true_type& /* is_fast_copy<BidirectionalIterator1,BidirectionalIterator2>() */)
{
// \todo Make sure memory ranges don't overlap, otherwise people should use move and move_backward.
static_assert(sizeof(typename iterator_traits<BidirectionalIterator1>::value_type) == sizeof(typename iterator_traits<BidirectionalIterator2>::value_type), "Size of value types must match for a trivial copy");
AZStd::size_t numElements = last - first;
if (numElements > 0)
else
{
result -= numElements;
AZ_Assert((&*result < &*first) || (&*result >= (&*first + numElements)), "AZStd::copy_backward memory overlaps use AZStd::copy!");
AZ_Assert(((&*result + numElements) <= &*first) || ((&*result + numElements) > (&*first + numElements)), "AZStd::copy_backward memory overlaps use AZStd::copy!");
/*AZSTD_STL::*/ memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits<BidirectionalIterator1>::value_type));
while (first != last)
{
*--result = *--last;
}
return result;
}
return result;
}
template <class BidirectionalIterator1, class ForwardIterator>
constexpr ForwardIterator reverse_copy(const BidirectionalIterator1& first, const BidirectionalIterator1& last, ForwardIterator dest)
constexpr ForwardIterator reverse_copy(BidirectionalIterator1 first, BidirectionalIterator1 last, ForwardIterator dest)
{
BidirectionalIterator1 iter(last);
while (iter != first)
while (last != first)
{
*(dest++) = *(--iter);
*(dest++) = *(--last);
}
return dest;
@@ -342,143 +335,209 @@ namespace AZStd
* Specialized algorithms 20.4.4. We extend that by adding faster specialized versions when we have trivial assign type.
*/
template <class InputIterator, class ForwardIterator>
constexpr ForwardIterator uninitialized_copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
constexpr ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result, bool)
{
InputIterator iter(first);
for (; iter != last; ++result, ++iter)
// Specialized copy for contiguous iterators which are trivially copyable
if constexpr (Internal::is_fast_copy_v<InputIterator, ForwardIterator>)
{
::new (static_cast<void*>(&*result)) typename iterator_traits<ForwardIterator>::value_type(*iter);
}
size_t numElements = last - first;
if (numElements > 0)
{
#if az_has_builtin_memcpy
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Value type sizes must match for a trivial copy");
__builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
#else
if (az_builtin_is_constant_evaluated())
{
for (; first != last; ++result, ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(result)), *first);
}
return result;
return result;
}
else
{
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Value type sizes must match for a trivial copy");
::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
}
#endif
}
return result + numElements;
}
else
{
for (; first != last; ++result, ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(result)), *first);
}
return result;
}
}
// Specialized copy for contiguous iterators and trivial copy type.
// This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers
template <class InputIterator, class ForwardIterator>
inline ForwardIterator uninitialized_copy(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
{
static_assert(sizeof(typename iterator_traits<InputIterator>::value_type) == sizeof(typename iterator_traits<ForwardIterator>::value_type), "Value type sizes must match for a trivial copy");
AZStd::size_t numElements = last - first;
if (numElements > 0)
{
/*AZSTD_STL::*/
memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits<InputIterator>::value_type));
}
return result + numElements;
}
template <class InputIterator, class ForwardIterator>
constexpr ForwardIterator uninitialized_copy(const InputIterator& first, const InputIterator& last, ForwardIterator result)
constexpr ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result)
{
return uninitialized_copy(first, last, result, Internal::is_fast_copy<InputIterator, ForwardIterator>());
return uninitialized_copy(first, last, result, {});
}
// 25.3.1 Copy
template<class InputIterator, class OutputIterator>
constexpr OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{
return AZStd::Internal::copy(first, last, result, AZStd::Internal::is_fast_copy<InputIterator, OutputIterator>());
return Internal::copy(first, last, result, {});
}
template <class BidirectionalIterator, class OutputIterator>
constexpr OutputIterator reverse_copy(BidirectionalIterator first, BidirectionalIterator last, OutputIterator dest)
{
return AZStd::Internal::reverse_copy(first, last, dest);
return Internal::reverse_copy(first, last, dest);
}
template<class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result)
{
return AZStd::Internal::copy_backward(first, last, result, AZStd::Internal::is_fast_copy<BidirectionalIterator1, BidirectionalIterator2>());
return Internal::copy_backward(first, last, result, {});
}
}
namespace AZStd::Internal
{
//////////////////////////////////////////////////////////////////////////
// Sequence move. If we use optimized version we use memmove.
// Sequence move
template <class InputIterator, class ForwardIterator>
constexpr ForwardIterator move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
constexpr ForwardIterator move(InputIterator first, InputIterator last, ForwardIterator result, bool)
{
InputIterator iter(first);
for (; iter != last; ++result, ++iter)
// Specialized copy for contiguous iterators which are trivially copyable
if constexpr (is_fast_copy_v<InputIterator, ForwardIterator>)
{
*result = AZStd::move(*iter);
size_t numElements = last - first;
if (numElements > 0)
{
#if az_has_builtin_memcpy
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Size of value types must match for a trivial copy");
__builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
#else
if (az_builtin_is_constant_evaluated())
{
for (; first != last; ++result, ++first)
{
*result = ::AZStd::move(*first);
}
return result;
}
else
{
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Size of value types must match for a trivial copy");
AZ_Assert((static_cast<const void*>(&*result) < static_cast<const void*>(&*first))
|| (static_cast<const void*>(&*result) >= static_cast<const void*>(&*first + numElements)),
"AZStd::move memory overlaps use AZStd::move_backward!");
::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
}
#endif
}
return result + numElements;
}
else
{
for (; first != last; ++result, ++first)
{
*result = ::AZStd::move(*first);
}
return result;
}
return result;
}
// Specialized copy for contiguous iterators (pointers) and trivial copy type.
// This overload cannot be constexpr until builtin_memmove is added to MSVC compilers
template <class InputIterator, class ForwardIterator>
inline ForwardIterator move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
{
static_assert(sizeof(typename iterator_traits<InputIterator>::value_type) == sizeof(typename iterator_traits<ForwardIterator>::value_type), "Size of value types must match for a trivial copy");
AZStd::size_t numElements = last - first;
if (numElements > 0)
{
/*AZSTD_STL::*/
memmove(&*result, &*first, numElements * sizeof(typename iterator_traits<InputIterator>::value_type));
}
return result + numElements;
}
// For generic iterators, move is the same as copy.
template <class BidirectionalIterator1, class BidirectionalIterator2>
constexpr BidirectionalIterator2 move_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const false_type& /* is_fast_copy<BidirectionalIterator1,BidirectionalIterator2>() */)
constexpr BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result, bool)
{
BidirectionalIterator1 iter(last);
while (first != iter)
// Specialized copy for contiguous iterators which are trivially copyable
if constexpr (is_fast_copy_v<BidirectionalIterator1, BidirectionalIterator1>)
{
*--result = AZStd::move(*--iter);
size_t numElements = last - first;
if (numElements > 0)
{
#if az_has_builtin_memmove
static_assert(sizeof(iter_value_t<BidirectionalIterator1>) == sizeof(iter_value_t<BidirectionalIterator2>), "Size of value types must match for a trivial copy");
result -= numElements;
__builtin_memmove(to_address(result), to_address(first), numElements * sizeof(iter_value_t<BidirectionalIterator1>));
#else
if (az_builtin_is_constant_evaluated())
{
while (first != last)
{
*--result = ::AZStd::move(*--last);
}
return result;
}
else
{
static_assert(sizeof(iter_value_t<BidirectionalIterator1>) == sizeof(iter_value_t<BidirectionalIterator2>), "Size of value types must match for a trivial copy");
result -= numElements;
AZ_Assert((static_cast<const void*>(&*result + numElements) <= static_cast<const void*>(&*first))
|| (static_cast<const void*>(&*result + numElements) > static_cast<const void*>(&*first + numElements)),
"AZStd::move_backward memory overlaps use AZStd::move!");
::memmove(to_address(result), to_address(first), numElements * sizeof(iter_value_t<BidirectionalIterator1>));
}
#endif
}
return result;
}
return result;
}
// Specialized copy for contiguous iterators (pointers) and trivial copy type.
// This overload cannot be constexpr until builtin_memmove is added to MSVC compilers
template <class BidirectionalIterator1, class BidirectionalIterator2>
inline BidirectionalIterator2 move_backward(const BidirectionalIterator1& first, const BidirectionalIterator1& last, BidirectionalIterator2 result, const true_type& /* is_fast_copy<BidirectionalIterator1,BidirectionalIterator2>() */)
{
// \todo Make sure memory ranges don't overlap, otherwise people should use move and move_backward.
static_assert(sizeof(typename iterator_traits<BidirectionalIterator1>::value_type) == sizeof(typename iterator_traits<BidirectionalIterator2>::value_type), "Size of value types must match for a trivial copy");
AZStd::size_t numElements = last - first;
result -= numElements;
if (numElements > 0)
else
{
/*AZSTD_STL::*/
memmove(&*result, &*first, numElements * sizeof(typename iterator_traits<BidirectionalIterator1>::value_type));
while (first != last)
{
*--result = ::AZStd::move(*--last);
}
return result;
}
return result;
}
template <class InputIterator, class ForwardIterator>
constexpr ForwardIterator uninitialized_move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const false_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
constexpr ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result, bool)
{
InputIterator iter(first);
for (; iter != last; ++result, ++iter)
// Specialized copy for contiguous iterators which are trivially copyable
if constexpr (is_fast_copy_v<InputIterator, ForwardIterator>)
{
::new (static_cast<void*>(&*result)) typename iterator_traits<ForwardIterator>::value_type(AZStd::move(*iter));
}
return result;
}
// Specialized copy for contiguous iterators and trivial move type. (since the object is POD we will just perform a copy)
// This overload cannot be constexpr until builtin_memcpy is added to MSVC compilers
template <class InputIterator, class ForwardIterator>
inline ForwardIterator uninitialized_move(const InputIterator& first, const InputIterator& last, ForwardIterator result, const true_type& /* is_fast_copy<InputIterator,ForwardIterator>() */)
{
static_assert(sizeof(typename iterator_traits<InputIterator>::value_type) == sizeof(typename iterator_traits<ForwardIterator>::value_type), "Value type sizes must match for a trivial copy");
AZStd::size_t numElements = last - first;
if (numElements > 0)
{
/*AZSTD_STL::*/
memcpy(&*result, &*first, numElements * sizeof(typename iterator_traits<InputIterator>::value_type));
}
return result + numElements;
}
size_t numElements = last - first;
if (numElements > 0)
{
#if az_has_builtin_memcpy
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Value type sizes must match for a trivial copy");
__builtin_memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
#else
if (az_builtin_is_constant_evaluated())
{
for (; first != last; ++result, ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(result)), ::AZStd::move(*first));
}
return result;
}
else
{
static_assert(sizeof(iter_value_t<InputIterator>) == sizeof(iter_value_t<ForwardIterator>), "Value type sizes must match for a trivial copy");
::memcpy(to_address(result), to_address(first), numElements * sizeof(iter_value_t<InputIterator>));
}
#endif
}
return result + numElements;
}
else
{
for (; first != last; ++result, ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(result)), ::AZStd::move(*first));
}
return result;
}
}
// end of sequence move.
//////////////////////////////////////////////////////////////////////////
}
@@ -492,19 +551,19 @@ namespace AZStd
template <typename InputIt, typename ForwardIt>
ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result)
{
return AZStd::Internal::uninitialized_move(first, last, result, AZStd::Internal::is_fast_copy<InputIt, InputIt>{});
return AZStd::Internal::uninitialized_move(first, last, result, {});
}
// 25.3.2 Move
template<class InputIterator, class OutputIterator>
OutputIterator move(InputIterator first, InputIterator last, OutputIterator result)
{
return AZStd::Internal::move(first, last, result, AZStd::Internal::is_fast_copy<InputIterator, OutputIterator>());
return AZStd::Internal::move(first, last, result, {});
}
template<class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result)
{
return AZStd::Internal::move_backward(first, last, result, AZStd::Internal::is_fast_copy<BidirectionalIterator1, BidirectionalIterator2>());
return AZStd::Internal::move_backward(first, last, result, {});
}
}
@@ -516,63 +575,77 @@ namespace AZStd::Internal
* Helper class to determine if we have apply fast fill. There are 3 conditions
* - trivial assign
* - size of type == 1 (chars) to use memset
* - contiguous iterators (pointers)
* - contiguous iterators
*/
template<class Out, class = void>
constexpr bool indirectly_copy_assignable = false;
template<class Out>
constexpr bool indirectly_copy_assignable<Out, enable_if_t<indirectly_readable<Out>>> =
is_trivially_copy_assignable_v<iter_value_t<Out>> && sizeof(iter_value_t<Out>) == 1;
template<class Iterator>
struct is_fast_fill_helper
{
using value_type = typename iterator_traits<Iterator>::value_type;
constexpr static bool value = is_trivially_copy_assignable_v<value_type> && sizeof(value_type) == 1
&& Internal::satisfies_contiguous_iterator_concept_v<Iterator>;
};
// Use this trait to to determine fill mode, based on the iterator, value size, etc.
// Use it when you call uninitialized_fill, uninitialized_fill_n, fill and fill_n.
template< typename Iterator >
struct is_fast_fill
: public ::AZStd::integral_constant<bool, ::AZStd::Internal::is_fast_fill_helper<Iterator>::value>
{};
using is_fast_fill = bool_constant<indirectly_copy_assignable<Iterator> && contiguous_iterator<Iterator>>;
template<class Iterator>
constexpr bool is_fast_fill_v = is_fast_fill<Iterator>::value;
// The fast fill trait is no longer used
// It is detected using C++20 concepts now
template <class ForwardIterator, class T>
constexpr void fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const false_type& /* is_fast_fill<ForwardIterator>() */)
constexpr void fill(ForwardIterator first, ForwardIterator last, const T& value, bool)
{
ForwardIterator iter(first);
for (; iter != last; ++iter)
if constexpr (is_fast_fill_v<ForwardIterator>)
{
*iter = value;
size_t numElements = last - first;
if (numElements > 0)
{
if (az_builtin_is_constant_evaluated())
{
for (; first != last; ++first)
{
*first = value;
}
}
else
{
::memset(to_address(first), reinterpret_cast<const unsigned char&>(value), numElements);
}
}
}
}
// Specialized version for character types where memset can be used
// This overload cannot be constexpr until builtin_memset is added to MSVC compilers
template <class ForwardIterator, class T>
inline void fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const true_type& /* is_fast_fill<ForwardIterator>() */)
{
AZStd::size_t numElements = last - first;
if (numElements > 0)
else
{
/*AZSTD_STL::*/
memset((void*)&*first, *reinterpret_cast<const unsigned char*>(&value), numElements);
for (; first != last; ++first)
{
*first = value;
}
}
}
template <class ForwardIterator, class Size, class T>
constexpr void fill_n(ForwardIterator first, Size numElements, const T& value, const false_type& /* is_fast_fill<ForwardIterator>() */)
constexpr void fill_n(ForwardIterator first, Size numElements, const T& value, bool)
{
for (; numElements--; ++first)
if constexpr (is_fast_fill_v<ForwardIterator>)
{
*first = value;
if (numElements)
{
if (az_builtin_is_constant_evaluated())
{
for (; numElements--; ++first)
{
*first = value;
}
}
else
{
::memset(to_address(first), reinterpret_cast<const unsigned char&>(value), numElements);
}
}
}
}
// Specialized version for character types where memset can be used to perform the fill
// This overload cannot be constexpr until builtin_memset is added to MSVC compilers
template <class ForwardIterator, class Size, class T>
inline void fill_n(ForwardIterator first, Size numElements, const T& value, const true_type& /* is_fast_fill<ForwardIterator>() */)
{
if (numElements > 0)
else
{
/*AZSTD_STL::*/
memset(&*first, *reinterpret_cast<const unsigned char*>(&value), numElements);
for (; numElements--; ++first)
{
*first = value;
}
}
}
}
@@ -580,78 +653,85 @@ namespace AZStd::Internal
namespace AZStd
{
template <class ForwardIterator, class T>
constexpr void fill(const ForwardIterator& first, const ForwardIterator& last, const T& value)
constexpr void fill(ForwardIterator first, ForwardIterator last, const T& value)
{
Internal::fill(first, last, value, Internal::is_fast_fill<ForwardIterator>());
Internal::fill(first, last, value, {});
}
template <class ForwardIterator, class Size, class T>
constexpr void fill_n(ForwardIterator first, Size numElements, const T& value)
{
Internal::fill_n(first, numElements, value, Internal::is_fast_fill<ForwardIterator>());
Internal::fill_n(first, numElements, value, {});
}
template <class ForwardIterator, class T>
constexpr void uninitialized_fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const false_type& /* is_fast_fill<ForwardIterator>() */)
constexpr void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& value, bool)
{
ForwardIterator iter(first);
for (; iter != last; ++iter)
if constexpr (Internal::is_fast_fill_v<ForwardIterator>)
{
::new (static_cast<void*>(&*iter)) typename iterator_traits<ForwardIterator>::value_type(value);
size_t numElements = last - first;
if (numElements > 0)
{
if (az_builtin_is_constant_evaluated())
{
for (; first != last; ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(first)), value);
}
}
else
{
::memset(to_address(first), reinterpret_cast<const unsigned char&>(value), numElements);
}
}
}
}
// Specialized overload for types which meet the following criteria.
// 1. Has it's iterator_traits<T>::iterator_concept type set to to contiguous_iterator_tag
// 2. Is trivially assignable
// 3. Has a sizeof(T) == 1
// In such a case memset can be used to fill in the data
// This overload cannot be constexpr until builtin_memset is added to MSVC compilers
template <class ForwardIterator, class T>
inline void uninitialized_fill(const ForwardIterator& first, const ForwardIterator& last, const T& value, const true_type& /* is_fast_fill<ForwardIterator>() */)
{
AZStd::size_t numElements = last - first;
if (numElements > 0)
else
{
/*AZSTD_STL::*/
memset(&*first, *reinterpret_cast<const unsigned char*>(&value), numElements);
for (; first != last; ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(first)), value);
}
}
}
template <class ForwardIterator, class Size, class T>
constexpr void uninitialized_fill(ForwardIterator first, Size numElements, const T& value)
{
return uninitialized_fill(first, numElements, value, Internal::is_fast_fill<ForwardIterator>());
return uninitialized_fill(first, numElements, value, {});
}
template <class ForwardIterator, class Size, class T>
constexpr void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value, const false_type& /* is_fast_fill<ForwardIterator>() */)
constexpr void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value, bool)
{
for (; numElements--; ++first)
if constexpr (Internal::is_fast_fill_v<ForwardIterator>)
{
::new (static_cast<void*>(&*first)) typename iterator_traits<ForwardIterator>::value_type(value);
if (numElements > 0)
{
if (az_builtin_is_constant_evaluated())
{
for (; numElements--; ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(first)), value);
}
}
else
{
::memset(to_address(first), reinterpret_cast<const unsigned char&>(value), numElements);
}
}
}
}
// Specialized overload for types which meet the following criteria.
// 1. Has it's iterator_traits<T>::iterator_concept type set to to contiguous_iterator_tag
// 2. Is trivially assignable
// 3. Has a sizeof(T) == 1
// In such a case memset can be used to fill in the data
template <class ForwardIterator, class Size, class T>
inline void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value, const true_type& /* is_fast_fill<ForwardIterator>() */)
{
if (numElements)
else
{
/*AZSTD_STL::*/
memset(&*first, *reinterpret_cast<const unsigned char*>(&value), numElements);
for (; numElements--; ++first)
{
construct_at(static_cast<iter_value_t<ForwardIterator>*>(to_address(first)), value);
}
}
}
template <class ForwardIterator, class Size, class T>
constexpr void uninitialized_fill_n(ForwardIterator first, Size numElements, const T& value)
{
return uninitialized_fill_n(first, numElements, value, Internal::is_fast_fill<ForwardIterator>());
return uninitialized_fill_n(first, numElements, value, {});
}
}
@@ -38,4 +38,12 @@ namespace AZStd
{
return Internal::INVOKE(Internal::InvokeTraits::forward<F>(f), Internal::InvokeTraits::forward<Args>(args)...);
}
// models the invocable concept
template <class F, class... Args>
/*concept*/ constexpr bool invocable = is_invocable_v<F, Args...>;
// models the regular_invocable concept
template <class F, class... Args>
/*concept*/ constexpr bool regular_invocable = invocable<F, Args...>;
}
+8 -55
View File
@@ -8,19 +8,19 @@
#pragma once
#include <AzCore/std/base.h>
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/typetraits/is_convertible.h>
#include <AzCore/std/typetraits/is_base_of.h> // use by ConstIteratorCast
#include <AzCore/std/iterator/iterator_primitives.h>
#include <AzCore/std/typetraits/is_base_of.h>
#include <AzCore/std/typetraits/is_convertible.h>
#include <AzCore/std/typetraits/remove_cv.h>
#include <AzCore/std/typetraits/is_reference.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/utils.h>
#include <iterator>
namespace AZStd
{
// Everything unless specified is based on C++ standard 24 (lib.iterators).
// Everything unless specified is based on C++ standard 20 (lib.iterators).
/// Identifying tag for input iterators.
using input_iterator_tag = std::input_iterator_tag;
@@ -51,16 +51,6 @@ namespace AZStd::Internal
typename Iterator::reference>
> = true;
template <typename Iterator, typename = void>
inline constexpr bool has_iterator_category_v = false;
template <typename Iterator>
inline constexpr bool has_iterator_category_v<Iterator, AZStd::void_t<typename Iterator::iterator_category>> = true;
template <typename Iterator, typename = void>
inline constexpr bool has_iterator_concept_v = false;
template <typename Iterator>
inline constexpr bool has_iterator_concept_v<Iterator, AZStd::void_t<typename Iterator::iterator_concept>> = true;
// Iterator iterator_category alias must be one of the iterator category tags
template <typename Iterator, bool>
struct iterator_traits_category_tags
@@ -98,6 +88,8 @@ namespace AZStd
struct iterator_traits
: Internal::iterator_traits_type_aliases<Iterator, Internal::has_iterator_type_aliases_v<Iterator>>
{
// Internal type alias meant to indicate that this is the primary template
using _is_primary_template = iterator_traits;
};
/**
@@ -114,45 +106,6 @@ namespace AZStd
using iterator_category = random_access_iterator_tag;
using iterator_concept = contiguous_iterator_tag;
};
}
namespace AZStd::Internal
{
// iterator_category tag testers
template <typename Iterator, typename Category, bool = has_iterator_category_v<iterator_traits<Iterator>>>
inline constexpr bool has_iterator_category_convertible_to_v = false;
template <typename Iterator, typename Category>
inline constexpr bool has_iterator_category_convertible_to_v<Iterator, Category, true> = is_convertible_v<typename iterator_traits<Iterator>::iterator_category, Category>;
template <typename Iterator>
inline constexpr bool is_input_iterator_v = has_iterator_category_convertible_to_v<Iterator, input_iterator_tag>;
template <typename Iterator>
inline constexpr bool is_forward_iterator_v = has_iterator_category_convertible_to_v<Iterator, forward_iterator_tag>;
template <typename Iterator>
inline constexpr bool is_bidirectional_iterator_v = has_iterator_category_convertible_to_v<Iterator, bidirectional_iterator_tag>;
template <typename Iterator>
inline constexpr bool is_random_access_iterator_v = has_iterator_category_convertible_to_v<Iterator, random_access_iterator_tag>;
template <typename Iterator>
inline constexpr bool is_contiguous_iterator_v = has_iterator_category_convertible_to_v<Iterator, contiguous_iterator_tag>;
template <typename Iterator>
inline constexpr bool is_exactly_input_iterator_v = has_iterator_category_convertible_to_v<Iterator, input_iterator_tag> && !has_iterator_category_convertible_to_v<Iterator, forward_iterator_tag>;
// iterator concept testers
template <typename Derived, typename Base>
inline constexpr bool derived_from = is_base_of_v<Base, Derived> && is_convertible_v<const volatile Derived*, const volatile Base*>;
template <typename Iterator, typename Concept, bool = has_iterator_concept_v<iterator_traits<Iterator>>>
inline constexpr bool satisfies_iterator_concept = false;
template <typename Iterator, typename Concept>
inline constexpr bool satisfies_iterator_concept<Iterator, Concept, true> = derived_from<typename iterator_traits<Iterator>::iterator_concept, Concept>;
template <typename Iterator>
inline constexpr bool satisfies_contiguous_iterator_concept_v = satisfies_iterator_concept<Iterator, contiguous_iterator_tag>;
}
namespace AZStd
@@ -0,0 +1,200 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/base.h>
#include <AzCore/std/ranges/iter_move.h>
#include <AzCore/std/typetraits/common_reference.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_array.h>
#include <AzCore/std/typetraits/is_class.h>
#include <AzCore/std/typetraits/is_enum.h>
#include <AzCore/std/typetraits/is_integral.h>
#include <AzCore/std/typetraits/is_object.h>
#include <AzCore/std/typetraits/is_lvalue_reference.h>
#include <AzCore/std/typetraits/is_rvalue_reference.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <AzCore/std/typetraits/is_void.h>
#include <AzCore/std/typetraits/remove_extent.h>
#include <AzCore/std/typetraits/void_t.h>
namespace AZStd
{
// Bring in std utility functions into AZStd namespace
using std::forward;
// forward declare iterator_traits to avoid iterator.h include
template <class I>
struct iterator_traits;
}
// C++20 range traits for iteratable types
namespace AZStd::Internal
{
// Models the can-reference concept which isn't available until C++20
// template <class T, class = void>
template <class T>
constexpr bool can_reference = true;
template <>
inline constexpr bool can_reference<void> = false;
// Models the dereferencable concept which isn't available until C++20
template <class T, class = void>
/*concept*/ constexpr bool dereferenceable = false;
template <class T>
constexpr bool dereferenceable<T, enable_if_t<can_reference<decltype(*declval<T>())>>> = true;
template <class T, class = void>
constexpr bool is_primary_template_v = false;
template <class T>
constexpr bool is_primary_template_v<T, enable_if_t<is_same_v<T, typename T::_is_primary_template>>> = true;
// indirectly readable traits
template <typename T, typename = void>
constexpr bool has_value_type_v = false;
template <typename T>
constexpr bool has_value_type_v<T, void_t<typename T::value_type>> = true;
template <typename T, typename = void>
constexpr bool has_element_type_v = false;
template <typename T>
constexpr bool has_element_type_v<T, void_t<typename T::element_type>> = true;
template <typename T, typename = void>
struct object_type_value_requires {};
template <typename T>
struct object_type_value_requires<T, enable_if_t<is_object_v<T>>>
{
using value_type = remove_cv_t<T>;
};
template <typename T, typename = void>
struct indirectly_readable_requires {};
template <typename T>
struct indirectly_readable_requires<T, enable_if_t<!is_primary_template_v<iterator_traits<T>>
&& is_void_v<void_t<typename iterator_traits<T>::value_type>> >>
{
// iterator_traits has been been specialized
using value_type = typename iterator_traits<T>::value_type;
};
template <typename T>
struct indirectly_readable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& is_array_v<T>>>
{
using value_type = remove_cv_t<remove_extent_t<T>>;
};
template <typename T>
struct indirectly_readable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& has_value_type_v<T> && !has_element_type_v<T>>>
: object_type_value_requires<typename T::value_type> {};
template <typename T>
struct indirectly_readable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& has_element_type_v<T> && !has_value_type_v<T>>>
: object_type_value_requires<typename T::element_type> {};
template <typename T>
struct indirectly_readable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& has_value_type_v<T>&& has_element_type_v<T>
&& same_as<remove_cv_t<typename T::element_type>, remove_cv_t<typename T::value_type>> >>
: object_type_value_requires<typename T::value_type> {};
// incrementable traits
template <typename T, typename = void>
constexpr bool has_difference_type_v = false;
template <typename T>
constexpr bool has_difference_type_v<T, void_t<typename T::difference_type>> = true;
template <typename T, typename = void>
struct object_type_difference_requires {};
template <typename T>
struct object_type_difference_requires<T, enable_if_t<is_object_v<T>>>
{
using difference_type = ptrdiff_t;
};
template <typename T, typename = void>
struct incrementable_requires {};
// iterator_traits has been specialized
template <typename T>
struct incrementable_requires<T, enable_if_t<!is_primary_template_v<iterator_traits<T>>
&& is_void_v<void_t<typename iterator_traits<T>::difference_type>> >>
{
using difference_type = typename iterator_traits<T>::difference_type;
};
template <typename T>
struct incrementable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& has_difference_type_v<T>>>
{
using difference_type = typename T::difference_type;
};
template <typename T>
struct incrementable_requires<T, enable_if_t<is_primary_template_v<iterator_traits<T>>
&& !has_difference_type_v<T>
&& integral<decltype(declval<T>() - declval<T>())> >>
{
using difference_type = make_signed_t<decltype(declval<T>() - declval<T>())>;
};
}
namespace AZStd
{
// indirectly_readable_traits for iter_value_t
template <typename T>
struct indirectly_readable_traits
: Internal::indirectly_readable_requires<T> {};
template <typename T>
struct indirectly_readable_traits<T*>
: Internal::object_type_value_requires<T> {};
template <typename T>
struct indirectly_readable_traits<const T>
: indirectly_readable_traits<T> {};
template <typename T>
using iter_value_t = typename indirectly_readable_traits<remove_cvref_t<T>>::value_type;
template <typename T>
using iter_reference_t = enable_if_t<Internal::dereferenceable<T>, decltype(*declval<T&>())>;
// incrementable_traits for iter_difference_t
template <typename T>
struct incrementable_traits
: Internal::incrementable_requires<T> {};
template <typename T>
struct incrementable_traits<T*>
: Internal::object_type_difference_requires<T> {};
template <typename T>
struct incrementable_traits<const T>
: incrementable_traits<T> {};
template <typename T>
using iter_difference_t = typename incrementable_traits<remove_cvref_t<T>>::difference_type;
template <typename T>
using iter_rvalue_reference_t = decltype(ranges::iter_move(declval<T&>()));
namespace Internal
{
// model the indirectly readable concept
template <class In, class = void>
constexpr bool indirectly_readable_impl = false;
template <class In>
constexpr bool indirectly_readable_impl<In, enable_if_t<same_as<decltype(*declval<In>()), iter_reference_t<In>>
&& same_as<decltype(AZStd::ranges::iter_move(declval<In>())), iter_rvalue_reference_t<In>>
&& common_reference_with<iter_reference_t<In>&&, iter_value_t<In>&>
&& common_reference_with<iter_reference_t<In>&&, iter_rvalue_reference_t<In>&>
&& common_reference_with<iter_rvalue_reference_t<In>&&, const iter_value_t<In>&>>> = true;
}
template <typename T>
using iter_common_reference_t = enable_if_t<Internal::indirectly_readable_impl<T>,
common_reference_t<iter_reference_t<T>, iter_value_t<T>&>>;
}
@@ -0,0 +1,81 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/base.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_class.h>
#include <AzCore/std/typetraits/is_enum.h>
#include <AzCore/std/typetraits/is_lvalue_reference.h>
#include <AzCore/std/typetraits/is_rvalue_reference.h>
#include <AzCore/std/typetraits/remove_cvref.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/utility/move.h>
#include <AzCore/std/utility/declval.h>
namespace AZStd
{
// Bring in std utility functions into AZStd namespace
using std::forward;
}
// C++20 range traits for iteratable types
namespace AZStd::ranges::Internal
{
void iter_move();
template <typename It, typename = void>
constexpr bool iter_move_adl = false;
template <typename It>
constexpr bool iter_move_adl<It, void_t<decltype(iter_move(declval<It>()))>> = true;
template <typename It, typename = void>
constexpr bool is_class_or_enum_with_iter_move_adl = false;
template <typename It>
constexpr bool is_class_or_enum_with_iter_move_adl<It, enable_if_t<iter_move_adl<It>
&& (is_class_v<remove_cvref_t<It>> || is_enum_v<remove_cvref_t<It>>)>>
= true;
struct iter_move_fn
{
template <typename It>
constexpr auto operator()(It&& it) const
->enable_if_t<is_class_or_enum_with_iter_move_adl<It>,
decltype(iter_move(AZStd::forward<It>(it)))>
{
return iter_move(AZStd::forward<It>(it));
}
template <typename It>
constexpr auto operator()(It&& it) const
->enable_if_t<!is_class_or_enum_with_iter_move_adl<It>&& is_lvalue_reference_v<decltype(*AZStd::forward<It>(it))>,
decltype(AZStd::move(*AZStd::forward<It>(it)))>
{
return AZStd::move(*AZStd::forward<It>(it));
}
template <typename It>
constexpr auto operator()(It&& it) const
->enable_if_t<!is_class_or_enum_with_iter_move_adl<It> && !is_lvalue_reference_v<decltype(*AZStd::forward<It>(it))>,
decltype(*AZStd::forward<It>(it))>
{
return *AZStd::forward<It>(it);
}
};
}
namespace AZStd::ranges
{
inline namespace customization_point_object
{
inline constexpr auto iter_move = Internal::iter_move_fn{};
}
}
File diff suppressed because it is too large Load Diff
@@ -74,7 +74,7 @@ namespace AZStd
constexpr basic_fixed_string(const_pointer ptr);
// #6
template<class InputIt, typename = enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_t>>>
template<class InputIt, typename = enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_t>>>
constexpr basic_fixed_string(InputIt first, InputIt last);
// #7
@@ -146,7 +146,7 @@ namespace AZStd
constexpr auto append(size_type count, Element ch) -> basic_fixed_string&;
template<class InputIt>
constexpr auto append(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
constexpr auto append(AZStd::initializer_list<Element> ilist) -> basic_fixed_string&;
constexpr auto assign(const basic_fixed_string& rhs) -> basic_fixed_string&;
@@ -161,7 +161,7 @@ namespace AZStd
constexpr auto assign(size_type count, Element ch) -> basic_fixed_string&;
template<class InputIt>
constexpr auto assign(InputIt first, InputIt last)
->enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
->enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
constexpr auto assign(AZStd::initializer_list<Element> ilist) -> basic_fixed_string&;
@@ -179,7 +179,7 @@ namespace AZStd
constexpr auto insert(const_iterator insertPos, size_type count, Element ch) -> iterator;
template<class InputIt>
constexpr auto insert(const_iterator insertPos, InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>;
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>;
constexpr auto insert(const_iterator insertPos, AZStd::initializer_list<Element> ilist) -> iterator;
@@ -215,7 +215,7 @@ namespace AZStd
constexpr auto replace(const_iterator first, const_iterator last, size_type count, Element ch) -> basic_fixed_string&;
template<class InputIt>
constexpr auto 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&>;
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>;
constexpr auto replace(const_iterator first, const_iterator last, AZStd::initializer_list<Element> ilist) -> basic_fixed_string&;
constexpr auto at(size_type offset) -> reference;
@@ -325,14 +325,14 @@ namespace AZStd
template<class Element, size_t MaxElementCount, class Traits>
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&>
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -461,14 +461,14 @@ namespace AZStd
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::assign(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
{
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -627,15 +627,15 @@ namespace AZStd
template<class Element, size_t MaxElementCount, class Traits>
template<class InputIt>
inline constexpr auto basic_fixed_string<Element, MaxElementCount, Traits>::insert(const_iterator insertPos,
InputIt first, InputIt last)-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>
InputIt first, InputIt last)-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>
{ // insert [_First, _Last) at _Where
size_type insertOffset = AZStd::distance(cbegin(), insertPos);
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -927,14 +927,14 @@ 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 replaceFirst, InputIt replaceLast) -> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, basic_fixed_string&>
InputIt replaceFirst, InputIt replaceLast) -> enable_if_t<input_iterator<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>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -114,28 +114,23 @@ namespace AZStd
assign(count, ch);
}
template<class InputIt, typename = enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_t>>>
template<class InputIt, typename = enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_t>>>
inline basic_string(InputIt first, InputIt last, const Allocator& alloc = Allocator())
: m_storage{ skip_element_tag{}, alloc }
{ // construct from [first, last)
assign(first, last);
}
inline basic_string(const_pointer first, const_pointer last)
{ // construct from [first, last), const pointers
assign(first, last - first);
}
inline basic_string(const this_type& rhs)
: m_storage{ skip_element_tag{}, rhs.m_storage.second() }
{
assign(rhs, 0, npos);
assign(rhs);
}
inline basic_string(this_type&& rhs)
: m_storage{ skip_element_tag{}, AZStd::move(rhs.m_storage.second()) }
: m_storage{ skip_element_tag{}, rhs.m_storage.second() }
{
assign(AZStd::forward<this_type>(rhs));
assign(AZStd::move(rhs));
}
inline basic_string(const this_type& rhs, size_type rhsOffset, size_type count = npos)
@@ -251,14 +246,14 @@ namespace AZStd
template<class InputIt>
inline auto append(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, this_type&>
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, this_type&>
{ // append [first, last)
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -299,7 +294,7 @@ namespace AZStd
inline this_type& assign(const this_type& rhs)
{
return assign(rhs, 0, npos);
return this != &rhs ? assign(rhs, 0, npos) : *this;
}
inline this_type& assign(basic_string_view<Element, Traits> view)
@@ -319,7 +314,8 @@ namespace AZStd
pointer rhsData = rhs.data();
// Memmove the right hand side string data if it is using the short string optimization
// Otherwise set the pointer to the right hand side
if (rhs.m_storage.first().ShortStringOptimizationActive())
if (rhs.m_storage.first().ShortStringOptimizationActive() ||
(get_allocator() != rhs.get_allocator() && !allocator_traits<allocator_type>::propagate_on_container_move_assignment::value))
{
Traits::move(data, rhsData, rhs.size() + 1); // string + null-terminator
}
@@ -395,14 +391,14 @@ namespace AZStd
template<class InputIt>
auto assign(InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, this_type&>
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, this_type&>
{
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<InputIt>)
{
// forward iterator pointer type doesn't match the const_pointer type
// So the elements need to be assigned one by one into the buffer
@@ -431,7 +427,7 @@ namespace AZStd
inputCopy.push_back(static_cast<Element>(*first));
}
return assign(inputCopy.c_str(), inputCopy.size());
return assign(AZStd::move(inputCopy));
}
}
inline this_type& insert(size_type offset, const this_type& rhs) { return insert(offset, rhs, 0, npos); }
@@ -539,15 +535,15 @@ namespace AZStd
template<class InputIt>
auto insert(const_iterator insertPos, InputIt first, InputIt last)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, iterator>
{ // insert [_First, _Last) at _Where
size_type insertOffset = AZStd::distance(cbegin(), insertPos);
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -834,14 +830,14 @@ namespace AZStd
template<class InputIt>
inline auto replace(const_iterator first, const_iterator last, InputIt replaceFirst, InputIt replaceLast)
-> enable_if_t<Internal::is_input_iterator_v<InputIt> && !is_convertible_v<InputIt, size_type>, this_type&>
-> enable_if_t<input_iterator<InputIt> && !is_convertible_v<InputIt, size_type>, this_type&>
{
if constexpr (Internal::satisfies_contiguous_iterator_concept_v<InputIt>
if constexpr (contiguous_iterator<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>)
else if constexpr (forward_iterator<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
@@ -1031,12 +1027,19 @@ namespace AZStd
// same allocator, swap storage
m_storage.first().swap(rhs.m_storage.first());
}
else if (allocator_traits<allocator_type>::propagate_on_container_swap::value)
{
// The allocator propagates on swap, so the allocators can be swapped
m_storage.first().swap(rhs.m_storage.first());
using AZStd::swap;
swap(m_storage.second(), rhs.m_storage.second());
}
else
{
// different allocator, do multiple assigns
this_type tmp = *this;
*this = rhs;
rhs = tmp;
this_type tmp = AZStd::move(*this);
*this = AZStd::move(rhs);
rhs = AZStd::move(tmp);
}
}
@@ -7,6 +7,7 @@
*/
#pragma once
#include <AzCore/std/ranges/ranges.h>
#include <AzCore/std/createdestroy.h>
#include <AzCore/std/iterator.h>
#include <AzCore/std/limits.h>
@@ -308,78 +309,87 @@ namespace AZStd
}
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
{
// In GCC versions prior to major version 10, __builtin_memcmp fails in valid checks in constexpr evaluation
#if !defined(AZ_COMPILER_GCC) || AZ_COMPILER_GCC >= 100000
if constexpr (AZStd::is_same_v<char_type, char>)
{
return __builtin_memcmp(s1, s2, count);
}
else if constexpr (AZStd::is_same_v<char_type, wchar_t>)
{
return __builtin_wmemcmp(s1, s2, count);
} else
#endif
{
if (az_builtin_is_constant_evaluated())
{
for (; count; --count, ++s1, ++s2)
static constexpr int compare(const char_type* s1, const char_type* s2, size_t count) noexcept
{
// In GCC versions , __builtin_memcmp fails in valid checks in constexpr evaluation
#if !defined(AZ_COMPILER_GCC)
if constexpr (AZStd::is_same_v<char_type, char>)
{
return __builtin_memcmp(s1, s2, count);
}
else if constexpr (AZStd::is_same_v<char_type, wchar_t>)
{
return __builtin_wmemcmp(s1, s2, count);
}
else
#endif
{
if (az_builtin_is_constant_evaluated())
{
for (; count; --count, ++s1, ++s2)
{
if (lt(*s1, *s2))
{
return -1;
}
else if (lt(*s2, *s1))
{
return 1;
}
}
return 0;
}
else
{
return ::memcmp(s1, s2, count * sizeof(char_type));
}
}
if (lt(*s1, *s2))
{
return -1;
}
else if (lt(*s2, *s1))
{
return 1;
}
}
return 0;
}
else
{
return ::memcmp(s1, s2, count * sizeof(char_type));
}
}
}
static constexpr size_t length(const char_type* s) noexcept
{
// For GCC versions less than 10, __builtin_strlen and __builtin_wcslen is not supported as const expressions
// so for that case it will need to manually count the characters (at compile time) instead
#if defined(AZ_COMPILER_GCC) && AZ_COMPILER_GCC < 100000
if constexpr (AZStd::is_same_v<char_type, char>)
{
#if defined(AZ_COMPILER_GCC) && AZ_COMPILER_GCC < 100000
if (!az_builtin_is_constant_evaluated())
{
return strlen(s);
}
else
{
size_t strLength{};
for (; *s; ++s, ++strLength)
{
;
}
return strLength;
}
#else
return __builtin_strlen(s);
#endif
}
else if constexpr (AZStd::is_same_v<char_type, wchar_t>)
{
#if defined(AZ_COMPILER_GCC)
if (!az_builtin_is_constant_evaluated())
{
return wcslen(s);
}
}
size_t strLength{};
for (; *s; ++s, ++strLength)
{
;
}
return strLength;
else
{
size_t strLength{};
for (; *s; ++s, ++strLength)
{
;
}
return strLength;
}
#else
if constexpr (AZStd::is_same_v<char_type, char>)
{
return __builtin_strlen(s);
}
else if constexpr (AZStd::is_same_v<char_type, wchar_t>)
{
return __builtin_wcslen(s);
#endif
}
else
{
@@ -390,46 +400,59 @@ namespace AZStd
}
return strLength;
}
#endif // defined(AZ_COMPILER_GCC) && AZ_COMPILER_GCC < 100000
}
static constexpr const char_type* find(const char_type* s, size_t count, const char_type& ch) noexcept
{
// For GCC versions less than 10, __builtin_char_memchr and __builtin_wmemchr is not supported, and
// __builtin_memchr is not supported as const expressions. In those cases we will manually locate and
// For GCC versions less than 10, __builtin_char_memchr and __builtin_wmemchr is not supported, and
// __builtin_memchr is not supported as const expressions. In those cases we will manually locate and
// return the pointer to 's' (at compile time)
#if defined(AZ_COMPILER_GCC) && AZ_COMPILER_GCC < 100000
if constexpr (AZStd::is_same_v<char_type, char>)
{
#if defined(AZ_COMPILER_GCC)
if (!az_builtin_is_constant_evaluated())
{
return static_cast<const char_type*>(__builtin_memchr(s, ch, count));
}
else
{
for (; count; --count, ++s)
{
if (eq(*s, ch))
{
return s;
}
}
return nullptr;
}
#else
return __builtin_char_memchr(s, ch, count);
#endif // defined(AZ_COMPILER_GCC)AZ_COMPILER_GCC < 100000
}
else if constexpr (AZStd::is_same_v<char_type, wchar_t>)
{
#if defined(AZ_COMPILER_GCC)
if (!az_builtin_is_constant_evaluated())
{
return wmemchr(s, ch, count);
}
}
for (; count; --count, ++s)
{
if (eq(*s, ch))
else
{
return s;
for (; count; --count, ++s)
{
if (eq(*s, ch))
{
return s;
}
}
return nullptr;
}
}
return nullptr;
#else
if constexpr (AZStd::is_same_v<char_type, char>)
{
return __builtin_char_memchr(s, ch, count);
}
else if constexpr (AZStd::is_same_v<char_type, wchar_t>)
{
return __builtin_wmemchr(s, ch, count);
#endif
}
else
{
@@ -440,9 +463,9 @@ namespace AZStd
return s;
}
}
return nullptr;
}
#endif
}
static constexpr char_type* move(char_type* dest, const char_type* src, size_t count) noexcept
{
@@ -452,7 +475,7 @@ namespace AZStd
return dest;
}
#if az_has_builtin_memmove
#if !defined(AZ_COMPILER_GCC) && 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
@@ -505,7 +528,7 @@ namespace AZStd
}
static constexpr char_type* copy(char_type* dest, const char_type* src, size_t count) noexcept
{
#if az_has_builtin_memcpy
#if !defined(AZ_COMPILER_GCC) && 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
@@ -535,7 +558,7 @@ namespace AZStd
static constexpr char_type* copy_backward(char_type* dest, const char_type* src, size_t count) noexcept
{
char_type* result = dest;
#if az_has_builtin_memmove
#if !defined(AZ_COMPILER_GCC) && az_has_builtin_memmove
__builtin_memmove(dest, src, count * sizeof(char_type));
#else
if (az_builtin_is_constant_evaluated())
@@ -613,8 +636,9 @@ namespace AZStd
{}
template <typename It, typename End, typename = AZStd::enable_if_t<
Internal::satisfies_contiguous_iterator_concept_v<It>
&& is_same_v<typename AZStd::iterator_traits<It>::value_type, value_type>
contiguous_iterator<It>
&& sized_sentinel_for<End, It>
&& is_same_v<iter_value_t<It>, value_type>
&& !is_convertible_v<End, size_type>>
>
constexpr basic_string_view(It first, End last)
@@ -961,23 +985,6 @@ namespace AZStd
using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
template<class Element, class Traits = AZStd::char_traits<Element>>
using basic_const_string = basic_string_view<Element, Traits>;
using const_string = string_view;
using const_wstring = wstring_view;
template <class Element, class Traits = AZStd::char_traits<Element>>
constexpr typename basic_string_view<Element, Traits>::const_iterator begin(basic_string_view<Element, Traits> sv)
{
return sv.begin();
}
template <class Element, class Traits = AZStd::char_traits<Element>>
constexpr typename basic_string_view<Element, Traits>::const_iterator end(basic_string_view<Element, Traits> sv)
{
return sv.end();
}
inline namespace literals
{
inline namespace string_view_literals
@@ -1024,6 +1031,15 @@ namespace AZStd
} // namespace AZStd
namespace AZStd::ranges
{
template <class Element, class Traits>
inline constexpr bool enable_borrowed_range<basic_string_view<Element, Traits>> = true;
template <class Element, class Traits>
inline constexpr bool enable_view<basic_string_view<Element, Traits>> = true;
}
//! Use this macro to simplify safe printing of a string_view which may not be null-terminated.
//! Example: AZStd::string::format("Safely formatted: %.*s", AZ_STRING_ARG(myString));
#define AZ_STRING_ARG(str) aznumeric_cast<int>(str.size()), str.data()
@@ -0,0 +1,230 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/typetraits/config.h>
#include <AzCore/std/typetraits/common_type.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_const.h>
#include <AzCore/std/typetraits/is_convertible.h>
#include <AzCore/std/typetraits/is_lvalue_reference.h>
#include <AzCore/std/typetraits/is_rvalue_reference.h>
#include <AzCore/std/typetraits/is_same.h>
#include <AzCore/std/typetraits/is_volatile.h>
#include <AzCore/std/typetraits/remove_reference.h>
#include <AzCore/std/typetraits/remove_cvref.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/utility/declval.h>
namespace AZStd
{
template <class T, class U, template<class> class TQual, template<class> class UQual>
struct basic_common_reference
{};
}
namespace AZStd::Internal
{
// const volatile and reference qualifier copy templates
template <class T, class QualType>
struct copy_cv_qual
{
using type = conditional_t<is_const_v<T>, conditional_t<is_volatile_v<T>, const volatile QualType, const QualType>,
conditional_t<is_volatile_v<T>, volatile QualType, QualType>>;
};
template <class T,class QualType>
using copy_cv_qual_t = typename copy_cv_qual<T, QualType>::type;
static_assert(is_same_v<copy_cv_qual_t<int, float>, float>);
static_assert(is_same_v<copy_cv_qual_t<const int, float>, const float>);
static_assert(is_same_v<copy_cv_qual_t<volatile int, float>, volatile float>);
static_assert(is_same_v<copy_cv_qual_t<const volatile int, float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<int, const float>, const float>);
static_assert(is_same_v<copy_cv_qual_t<const int, const float>, const float>);
static_assert(is_same_v<copy_cv_qual_t<volatile int, const float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<const volatile int, const float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<int, volatile float>, volatile float>);
static_assert(is_same_v<copy_cv_qual_t<const int, volatile float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<volatile int, volatile float>, volatile float>);
static_assert(is_same_v<copy_cv_qual_t<const volatile int, volatile float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<int, const volatile float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<const int, const volatile float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<volatile int, const volatile float>, const volatile float>);
static_assert(is_same_v<copy_cv_qual_t<const volatile int, const volatile float>, const volatile float>);
template <class T, class QualType>
struct copy_reference_qual
{
using type = conditional_t<is_lvalue_reference_v<T>, QualType&,
conditional_t<is_rvalue_reference_v<T>, QualType&&, QualType>>;
};
template <class T, class QualType>
using copy_reference_qual_t = typename copy_reference_qual<T, QualType>::type;
static_assert(is_same_v<copy_reference_qual_t<int, float>, float>);
static_assert(is_same_v<copy_reference_qual_t<int&, float>, float&>);
static_assert(is_same_v<copy_reference_qual_t<int&&, float>, float&&>);
static_assert(is_same_v<copy_reference_qual_t<int, float&>, float&>);
static_assert(is_same_v<copy_reference_qual_t<int&, float&>, float&>);
static_assert(is_same_v<copy_reference_qual_t<int&&, float&>, float&>);
static_assert(is_same_v<copy_reference_qual_t<int, float&&>, float&&>);
static_assert(is_same_v<copy_reference_qual_t<int&, float&&>, float&>);
static_assert(is_same_v<copy_reference_qual_t<int&&, float&&>, float&&>);
template <class T, class QualType>
using copy_cvref_qual_t = copy_cv_qual_t<copy_reference_qual_t<T, QualType>, QualType>;
template <class T>
struct copy_qualifiers_from_t
{
template <class X>
using templ = copy_cvref_qual_t<T, X>;
};
template <class T, class U>
using cond_res = decltype(false ? declval<copy_cv_qual_t<remove_reference_t<T>, remove_reference_t<U>>&>()
: declval<copy_cv_qual_t<remove_reference_t<U>, remove_reference_t<T>>&>());
// common reference helper templates begin
template <class T, class U, typename = void>
struct common_reference_base_reference_test;
// COMMON_REF is defined within the C++ standard at https://eel.is/c++draft/meta.trans.other#3.5
template <class T, class U>
struct common_reference_base_reference_test<T, U,
enable_if_t<is_lvalue_reference_v<T>&& is_lvalue_reference_v<U>,
void_t<cond_res<T,U>> >>
{
// Uses the ternary operator for determining the common type
using type = cond_res<T, U>;
};
template <class T, class U>
struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T>&& is_rvalue_reference_v<U>>>
{
using C = remove_reference_t<typename common_reference_base_reference_test<remove_reference_t<T>&, remove_reference_t<U>&>::type>;
using type = AZStd::enable_if_t<is_convertible_v<T, C>&& is_convertible_v<U, C>, C>;
};
template <class T, class U>
struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T>&& is_lvalue_reference_v<U>>>
{
// Turn rvalue references to const lvalue references
using D = typename common_reference_base_reference_test<const remove_reference_t<T>&, remove_reference_t<U>&>::type;
using type = AZStd::enable_if_t<is_convertible_v<T, D>, D>;
};
template <class T, class U>
struct common_reference_base_reference_test<T, U, enable_if_t<is_lvalue_reference_v<T>&& is_rvalue_reference_v<U>>>
{
// Swap the parameters to call the 3rd specialization for common_reference_base_reference_test
using type = typename common_reference_base_reference_test<U, T>::type;
};
template <class T, class U, typename = void>
constexpr bool has_reference_test = false;
template <class T, class U>
constexpr bool has_reference_test<T, U, void_t<typename common_reference_base_reference_test<T, U>::type>> = true;
template <class T, class U, typename = void>
struct basic_common_reference_test;
template <class T, class U>
struct basic_common_reference_test<T, U, void_t<typename basic_common_reference<remove_cvref_t<T>, remove_cvref_t<U>,
copy_qualifiers_from_t<T>::template templ, copy_qualifiers_from_t<U>::template templ>::type>>
{
using type = typename basic_common_reference<remove_cvref_t<T>, remove_cvref_t<U>,
copy_qualifiers_from_t<T>::template templ, copy_qualifiers_from_t<U>::template templ>::type;
};
template <class T, class U, typename = void>
constexpr bool has_basic_common_reference_test = false;
template <class T, class U>
constexpr bool has_basic_common_reference_test<T, U,
void_t<typename basic_common_reference_test<T, U>::type>> = true;
template <class T, class U, typename = void>
constexpr bool has_condition_result_test = false;
template <class T, class U>
constexpr bool has_condition_result_test<T, U, void_t<decltype(false ? declval<T>() : declval<U>())>> = true;
template <class T, class U, typename = void>
struct common_reference_base_test
{};
template <class T, class U>
struct common_reference_base_test<T, U, enable_if_t<has_reference_test<T, U>>>
: common_reference_base_reference_test<T, U>
{};
template <class T, class U>
struct common_reference_base_test<T, U, enable_if_t<!has_reference_test<T, U>
&& has_basic_common_reference_test<T, U>>>
: basic_common_reference_test<T, U>
{};
template <class T, class U>
struct common_reference_base_test<T, U, enable_if_t<!has_reference_test<T, U>
&& !has_basic_common_reference_test<T, U> && has_condition_result_test<T,U>>>
{
using type = decltype(false ? declval<T>() : declval<U>());
};
template <class T, class U>
struct common_reference_base_test<T, U, enable_if_t<!has_reference_test<T, U>
&& !has_basic_common_reference_test<T, U> && !has_condition_result_test<T, U>>>
: common_type<T, U>
{};
template <class... T>
struct common_reference_base
{};
template <class T>
struct common_reference_base<T>
{
using type = T;
};
template <class T, class U>
struct common_reference_base<T, U>
: common_reference_base_test<T, U>
{};
template <class T, class U, class V, class... Rs>
struct common_reference_base<T, U, V, Rs...>
: common_reference_base<typename common_reference_base<T, U>::type, V, Rs...>
{};
}
namespace AZStd
{
template <class... T>
struct common_reference
: Internal::common_reference_base<T...>
{};
template <class... T>
using common_reference_t = typename common_reference<T...>::type;
// models the common reference concept
namespace Internal
{
template<class T, class U, typename = void>
constexpr bool common_reference_with_impl = false;
template<class T, class U>
constexpr bool common_reference_with_impl<T, U, enable_if_t<
same_as<common_reference_t<T, U>, common_reference_t<U, T>>
&& convertible_to<T, common_reference_t<T, U>>
&& convertible_to<U, common_reference_t<T, U>>
>> = true;
}
template<class T, class U>
/*concept*/ constexpr bool common_reference_with = Internal::common_reference_with_impl<T, U>;
}
@@ -8,12 +8,26 @@
#pragma once
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/intrinsics.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/utility/declval.h>
namespace AZStd
{
using std::is_convertible;
using std::is_convertible_v;
// models the C++20 convertible_to concept
namespace Internal
{
template<typename From, typename To, typename = void>
constexpr bool convertible_to_impl = false;
template<typename From, typename To>
constexpr bool convertible_to_impl<From, To, enable_if_t<
is_convertible_v<From, To>, void_t<decltype(static_cast<To>(declval<From>()))>>> = true;
}
template<typename From, typename To>
constexpr bool is_convertible_v = std::is_convertible_v<From, To>;
/*concept*/ constexpr bool convertible_to = Internal::convertible_to_impl<From, To>;
}
@@ -21,4 +21,7 @@ namespace AZStd
constexpr bool is_trivially_destructible_v = std::is_trivially_destructible<T>::value;
template<class T>
constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible<T>::value;
template<class T>
/*concept*/ constexpr bool destructible = is_nothrow_destructible_v<T>;
}
@@ -13,4 +13,7 @@ namespace AZStd
{
using std::is_floating_point;
using std::is_floating_point_v;
template<class T>
/*concept*/ constexpr bool floating_point = is_floating_point_v<T>;
}
@@ -13,4 +13,7 @@ namespace AZStd
{
using std::is_integral;
using std::is_integral_v;
template<class T>
/*concept*/ constexpr bool integral = is_integral_v<T>;
}
@@ -13,4 +13,8 @@ namespace AZStd
{
using std::is_same;
using std::is_same_v;
// models the same_as concept
template <class T, class U>
/*concept*/ constexpr bool same_as = is_same_v<T, U>;
}
@@ -27,6 +27,8 @@
#include <AzCore/std/typetraits/add_volatile.h>
#include <AzCore/std/typetraits/alignment_of.h>
#include <AzCore/std/typetraits/aligned_storage.h>
#include <AzCore/std/typetraits/common_reference.h>
#include <AzCore/std/typetraits/common_type.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/conjunction.h>
#include <AzCore/std/typetraits/decay.h>
@@ -7,4 +7,9 @@
*/
#pragma once
#include <../Common/Default/AzCore/IO/Streamer/StreamerContext_Default.h>
#include <utility>
namespace AZStd
{
using std::declval;
}
@@ -5,21 +5,15 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef GM_CRIPTER_INTERFACE_H
#define GM_CRIPTER_INTERFACE_H
#pragma once
#include <GridMate/Types.h>
namespace GridMate
namespace AZStd
{
/**
* Traffic control interface
*/
class Cripter
// rvalue
// rvalue move
template<class T>
constexpr AZStd::remove_reference_t<T>&& move(T&& t)
{
public:
};
return static_cast<AZStd::remove_reference_t<T>&&>(t);
}
}
#endif // GM_CRIPTER_INTERFACE_H
+2 -9
View File
@@ -22,22 +22,15 @@
#include <AzCore/std/typetraits/is_convertible.h>
#include <AzCore/std/typetraits/is_lvalue_reference.h>
#include <AzCore/std/typetraits/void_t.h>
#include <AzCore/std/utility/declval.h>
#include <AzCore/std/utility/move.h>
#include <utility>
namespace AZStd
{
//////////////////////////////////////////////////////////////////////////
// rvalue
// rvalue move
template<class T>
constexpr AZStd::remove_reference_t<T>&& move(T && t)
{
return static_cast<AZStd::remove_reference_t<T>&&>(t);
}
using std::forward;
using std::declval;
using std::exchange;
template <class T>
@@ -116,7 +116,7 @@ namespace AZ
const char* GetObbStoragePath() const { return m_obbStoragePath.c_str(); }
//! Get the dot separated package name for the current application.
//! e.g. com.lumberyard.samples for SamplesProject
//! e.g. org.o3de.samples for SamplesProject
const char* GetPackageName() const { return m_packageName.c_str(); }
//! Get the app version code (android:versionCode in the manifest).

Some files were not shown because too many files have changed in this diff Show More