Merge branch 'development' into Prefabs/PlayInEditorMissingAssets
This commit is contained in:
@@ -1,439 +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 <errno.h> // for EACCES
|
||||
|
||||
#include <AzCore/Android/APKFileHandler.h>
|
||||
#include <AzCore/Android/JNI/Object.h>
|
||||
#include <AzCore/Android/Utils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
//Note: Switching on verbose logging will give you a lot of detailed information about what files are being read from the APK
|
||||
// but there is a likelihood it could cause logcat to terminate with a 'buffer full' error. Restarting logcat will resume logging
|
||||
// but you may lose information
|
||||
#define VERBOSE_IO_LOGGING 0
|
||||
|
||||
#if VERBOSE_IO_LOGGING
|
||||
#define FILE_IO_LOG(...) AZ_Printf("LMBR", __VA_ARGS__)
|
||||
#else
|
||||
#define FILE_IO_LOG(...)
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
AZ::EnvironmentVariable<APKFileHandler> APKFileHandler::s_instance;
|
||||
|
||||
|
||||
bool APKFileHandler::Create()
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<APKFileHandler>(AZ::AzTypeInfo<APKFileHandler>::Name());
|
||||
}
|
||||
|
||||
if (s_instance->IsReady()) // already created in a different module
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return s_instance->Initialize();
|
||||
}
|
||||
|
||||
void APKFileHandler::Destroy()
|
||||
{
|
||||
s_instance.Reset();
|
||||
}
|
||||
|
||||
bool APKFileHandler::ShouldLoadFileToMemory(const char* filePath)
|
||||
{
|
||||
if (!filePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const AZStd::string& fileName : m_memFileNames)
|
||||
{
|
||||
if (strstr(filePath, fileName.c_str()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryBuffer* APKFileHandler::GetInMemoryFileBuffer(void* asset)
|
||||
{
|
||||
for (auto it = m_memFileBuffers.begin(); it != m_memFileBuffers.end(); it++)
|
||||
{
|
||||
if (it->m_asset == asset)
|
||||
{
|
||||
return &(*it);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void APKFileHandler::RemoveInMemoryFileBuffer(void* asset)
|
||||
{
|
||||
for (auto it = m_memFileBuffers.begin(); it != m_memFileBuffers.end(); it++)
|
||||
{
|
||||
if (it->m_asset == asset)
|
||||
{
|
||||
m_memFileBuffers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FILE* APKFileHandler::Open(const char* filename, const char* mode, AZ::u64& size)
|
||||
{
|
||||
ANDROID_IO_PROFILE_SECTION_ARGS("APK Open");
|
||||
FILE* fileHandle = nullptr;
|
||||
|
||||
if (mode[0] != 'w')
|
||||
{
|
||||
FILE_IO_LOG("******* Attempting to open file in APK:[%s] ", filename);
|
||||
|
||||
AAsset* asset = nullptr;
|
||||
bool loadFileToMemory = Get().ShouldLoadFileToMemory(filename);
|
||||
int assetMode = loadFileToMemory ? AASSET_MODE_BUFFER : AASSET_MODE_UNKNOWN;
|
||||
|
||||
asset = AAssetManager_open(Utils::GetAssetManager(), Utils::StripApkPrefix(filename).c_str(), assetMode);
|
||||
|
||||
if (asset != nullptr)
|
||||
{
|
||||
// the pointer returned by funopen will allow us to use fread, fseek etc
|
||||
fileHandle = funopen(asset, APKFileHandler::Read, APKFileHandler::Write, APKFileHandler::Seek, APKFileHandler::Close);
|
||||
|
||||
if (loadFileToMemory)
|
||||
{
|
||||
MemoryBuffer buf;
|
||||
buf.m_buffer = (char*)AAsset_getBuffer(asset);
|
||||
buf.m_totalSize = AAsset_getLength(asset);
|
||||
buf.m_asset = asset;
|
||||
if (buf.m_buffer)
|
||||
{
|
||||
Get().m_memFileBuffers.push_back(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to load %s to memory", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// the file pointer we return from funopen can't be used to get the length of the file so we need to capture that info while we have the AAsset pointer available
|
||||
size = static_cast<AZ::u64>(AAsset_getLength64(asset));
|
||||
FILE_IO_LOG("File loaded successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
FILE_IO_LOG("####### Failed to open file in APK:[%s] ", filename);
|
||||
}
|
||||
}
|
||||
return fileHandle;
|
||||
}
|
||||
|
||||
int APKFileHandler::Read(void* asset, char* buffer, int size)
|
||||
{
|
||||
ANDROID_IO_PROFILE_SECTION_ARGS("APK Read");
|
||||
|
||||
APKFileHandler& apkHandler = Get();
|
||||
|
||||
if (apkHandler.m_numBytesToRead < size && apkHandler.m_numBytesToRead > 0)
|
||||
{
|
||||
size = apkHandler.m_numBytesToRead;
|
||||
}
|
||||
apkHandler.m_numBytesToRead -= size;
|
||||
|
||||
MemoryBuffer* buf = apkHandler.GetInMemoryFileBuffer(asset);
|
||||
if (buf)
|
||||
{
|
||||
const char* tempBuf = buf->m_buffer + buf->m_offset;
|
||||
memcpy(buffer, tempBuf, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
return AAsset_read(static_cast<AAsset*>(asset), buffer, static_cast<size_t>(size));
|
||||
}
|
||||
|
||||
int APKFileHandler::Write(void* asset, const char* buffer, int size)
|
||||
{
|
||||
return EACCES;
|
||||
}
|
||||
|
||||
fpos_t APKFileHandler::Seek(void* asset, fpos_t offset, int origin)
|
||||
{
|
||||
ANDROID_IO_PROFILE_SECTION_ARGS("APK Seek");
|
||||
MemoryBuffer* buf = Get().GetInMemoryFileBuffer(asset);
|
||||
if (buf)
|
||||
{
|
||||
if (origin == SEEK_SET)
|
||||
{
|
||||
buf->m_offset = offset;
|
||||
}
|
||||
else if (origin == SEEK_CUR)
|
||||
{
|
||||
buf->m_offset += offset;
|
||||
}
|
||||
else if (origin == SEEK_END)
|
||||
{
|
||||
buf->m_offset = buf->m_totalSize - offset;
|
||||
}
|
||||
|
||||
if (buf->m_offset > buf->m_totalSize)
|
||||
{
|
||||
buf->m_offset = buf->m_totalSize;
|
||||
}
|
||||
if (buf->m_offset < 0)
|
||||
{
|
||||
buf->m_offset = 0;
|
||||
}
|
||||
|
||||
return buf->m_offset;
|
||||
}
|
||||
|
||||
return AAsset_seek(static_cast<AAsset*>(asset), offset, origin);
|
||||
}
|
||||
|
||||
int APKFileHandler::Close(void* asset)
|
||||
{
|
||||
Get().RemoveInMemoryFileBuffer(asset);
|
||||
|
||||
AAsset_close(static_cast<AAsset*>(asset));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APKFileHandler::FileLength(const char* filename)
|
||||
{
|
||||
AZ::u64 size = 0;
|
||||
FILE* asset = Open(filename, "r", size);
|
||||
if (asset != nullptr)
|
||||
{
|
||||
fclose(asset);
|
||||
}
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
AZ::IO::Result APKFileHandler::ParseDirectory(const char* path, FindDirsCallbackType findCallback)
|
||||
{
|
||||
ANDROID_IO_PROFILE_SECTION_ARGS("APK ParseDirectory");
|
||||
FILE_IO_LOG("********* About to search for file in [%s] ******* ", path);
|
||||
|
||||
APKFileHandler& apkHandler = Get();
|
||||
|
||||
DirectoryCache::const_iterator it = apkHandler.m_cachedDirectories.find(path);
|
||||
if (it == apkHandler.m_cachedDirectories.end())
|
||||
{
|
||||
// The NDK version of the Asset Manager only returns files and not directories so we must use the Java version to get all the data we need
|
||||
JNIEnv* jniEnv = JNI::GetEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
return AZ::IO::ResultCode::Error;
|
||||
}
|
||||
|
||||
auto newDirectory = apkHandler.m_cachedDirectories.emplace(path, StringVector());
|
||||
jstring dirPath = jniEnv->NewStringUTF(path);
|
||||
jobjectArray javaFileListObject = apkHandler.m_javaInstance->InvokeStaticObjectMethod<jobjectArray>("GetFilesAndDirectoriesInPath", dirPath);
|
||||
jniEnv->DeleteLocalRef(dirPath);
|
||||
|
||||
int numObjects = jniEnv->GetArrayLength(javaFileListObject);
|
||||
bool parseResults = true;
|
||||
|
||||
for (int i = 0; i < numObjects; i++)
|
||||
{
|
||||
if (!parseResults)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
jstring str = static_cast<jstring>(jniEnv->GetObjectArrayElement(javaFileListObject, i));
|
||||
const char* entryName = jniEnv->GetStringUTFChars(str, 0);
|
||||
newDirectory.first->second.push_back(StringType(entryName));
|
||||
|
||||
parseResults = findCallback(entryName);
|
||||
|
||||
jniEnv->ReleaseStringUTFChars(str, entryName);
|
||||
jniEnv->DeleteLocalRef(str);
|
||||
}
|
||||
|
||||
jniEnv->DeleteGlobalRef(javaFileListObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool parseResults = true;
|
||||
|
||||
for (int i = 0; i < it->second.size(); i++)
|
||||
{
|
||||
if (!parseResults)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
parseResults = findCallback(it->second[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::IO::ResultCode::Success;
|
||||
}
|
||||
|
||||
bool APKFileHandler::IsDirectory(const char* path)
|
||||
{
|
||||
ANDROID_IO_PROFILE_SECTION_ARGS("APK IsDir");
|
||||
|
||||
APKFileHandler& apkHandler = Get();
|
||||
|
||||
DirectoryCache::const_iterator it = apkHandler.m_cachedDirectories.find(path);
|
||||
if (it == apkHandler.m_cachedDirectories.end())
|
||||
{
|
||||
JNIEnv* jniEnv = JNI::GetEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
jstring dirPath = jniEnv->NewStringUTF(path);
|
||||
jboolean isDir = apkHandler.m_javaInstance->InvokeStaticBooleanMethod("IsDirectory", dirPath);
|
||||
jniEnv->DeleteLocalRef(dirPath);
|
||||
|
||||
FILE_IO_LOG("########### [%s] %s a directory ######### ", path, retVal ? "IS" : "IS NOT");
|
||||
|
||||
return (isDir == JNI_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (it->second.size() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool APKFileHandler::DirectoryOrFileExists(const char* path)
|
||||
{
|
||||
ANDROID_IO_PROFILE_SECTION_ARGS("APK DirOrFileExists");
|
||||
|
||||
AZ::IO::FixedMaxPath insideApkPath(Utils::StripApkPrefix(path));
|
||||
|
||||
// Check for the case where the input path is equal to the APK Assets Prefix of /APK
|
||||
// In that case the directory is the "root" of APK assets in which case the directory exist
|
||||
if (insideApkPath.empty() && Utils::IsApkPath(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString filename{ insideApkPath.Filename().Native() };
|
||||
AZ::IO::FixedMaxPathString pathToFile{ insideApkPath.ParentPath().Native() };
|
||||
bool foundFile = false;
|
||||
|
||||
ParseDirectory(pathToFile.c_str(), [&](const char* name)
|
||||
{
|
||||
if (strcasecmp(name, filename.c_str()) == 0)
|
||||
{
|
||||
foundFile = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
FILE_IO_LOG("########### Directory or file [%s] %s exist ######### ", filename.c_str(), foundFile ? "DOES" : "DOES NOT");
|
||||
return foundFile;
|
||||
}
|
||||
|
||||
void APKFileHandler::SetNumBytesToRead(const size_t numBytesToRead)
|
||||
{
|
||||
// WARNING: This isn't a thread safe way of handling this problem, LY-65478 will fix it
|
||||
APKFileHandler& apkHandler = Get();
|
||||
apkHandler.m_numBytesToRead = numBytesToRead;
|
||||
}
|
||||
|
||||
void APKFileHandler::SetLoadFilesToMemory(const char* fileNames)
|
||||
{
|
||||
AZStd::string names(fileNames);
|
||||
size_t pos = 0;
|
||||
bool stringProcessed = false;
|
||||
APKFileHandler& apkHandler = Get();
|
||||
|
||||
while (!stringProcessed)
|
||||
{
|
||||
size_t newPos = names.find_first_of(',', pos);
|
||||
size_t len = 0;
|
||||
if (newPos == AZStd::string::npos)
|
||||
{
|
||||
len = newPos;
|
||||
stringProcessed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = newPos - pos;
|
||||
}
|
||||
AZStd::string fileName = names.substr(pos, len);
|
||||
pos = newPos + 1;
|
||||
apkHandler.m_memFileNames.push_back(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
APKFileHandler::APKFileHandler()
|
||||
: m_javaInstance()
|
||||
, m_cachedDirectories()
|
||||
, m_numBytesToRead(0)
|
||||
{
|
||||
}
|
||||
|
||||
APKFileHandler::~APKFileHandler()
|
||||
{
|
||||
m_memFileBuffers.set_capacity(0);
|
||||
m_memFileNames.set_capacity(0);
|
||||
|
||||
if (s_instance)
|
||||
{
|
||||
AZ_Assert(s_instance.IsOwner(), "The Android APK file handler instance is being destroyed by someone other than the owner.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
APKFileHandler& APKFileHandler::Get()
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::FindVariable<APKFileHandler>(AZ::AzTypeInfo<APKFileHandler>::Name());
|
||||
AZ_Assert(s_instance, "The Android APK file handler is NOT ready for use! Call Create first!");
|
||||
}
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
|
||||
bool APKFileHandler::Initialize()
|
||||
{
|
||||
JniObject* apkHandler = aznew JniObject("com/amazon/lumberyard/io/APKHandler", "APKHandler");
|
||||
if (!apkHandler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_javaInstance.reset(apkHandler);
|
||||
|
||||
m_javaInstance->RegisterStaticMethod("IsDirectory", "(Ljava/lang/String;)Z");
|
||||
m_javaInstance->RegisterStaticMethod("GetFilesAndDirectoriesInPath", "(Ljava/lang/String;)[Ljava/lang/String;");
|
||||
|
||||
#if VERBOSE_IO_LOGGING
|
||||
m_javaInstance->RegisterStaticField("s_debug", "Z");
|
||||
m_javaInstance->SetStaticBooleanField("s_debug", JNI_TRUE);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APKFileHandler::IsReady() const
|
||||
{
|
||||
return (m_javaInstance != nullptr);
|
||||
}
|
||||
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
@@ -1,166 +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/Android/JNI/JNI.h>
|
||||
#include <AzCore/Android/JNI/Object_fwd.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
|
||||
|
||||
//NOTE: When running with the RAD Telemetry Gem enabled, a lot of the file IO methods will be captured for analysis.
|
||||
// However developers who want to profile performance of their game when using APK's containing assets can enable the flag
|
||||
// below to instrument their game in even more detail
|
||||
#define AZ_ENABLED_VERBOSE_ANDROID_IO_PROFILING 0
|
||||
|
||||
#if AZ_ENABLED_VERBOSE_ANDROID_IO_PROFILING
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
|
||||
#define ANDROID_IO_PROFILE_SECTION AZ_PROFILE_FUNCTION(AzCore)
|
||||
#define ANDROID_IO_PROFILE_SECTION_ARGS(...) AZ_PROFILE_SCOPE(AzCore, __VA_ARGS__)
|
||||
#else
|
||||
#define ANDROID_IO_PROFILE_SECTION
|
||||
#define ANDROID_IO_PROFILE_SECTION_ARGS(...)
|
||||
#endif
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
struct MemoryBuffer
|
||||
{
|
||||
const char* m_buffer;
|
||||
AAsset* m_asset;
|
||||
int m_totalSize;
|
||||
int m_offset;
|
||||
|
||||
MemoryBuffer()
|
||||
{
|
||||
m_offset = 0;
|
||||
m_totalSize = 0;
|
||||
m_buffer = nullptr;
|
||||
m_asset = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class APKFileHandler
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(APKFileHandler, "{D16233A2-A183-40FE-8CF4-ABE8D53AB5B5}")
|
||||
AZ_CLASS_ALLOCATOR(APKFileHandler, AZ::OSAllocator, 0);
|
||||
|
||||
|
||||
typedef AZStd::function<bool(const char*)> FindDirsCallbackType;
|
||||
|
||||
|
||||
//! The preferred entry point for the construction of the global APKFileHandler instance
|
||||
static bool Create();
|
||||
|
||||
//! Public accessor to destroy the APKFileHandler global instance
|
||||
static void Destroy();
|
||||
|
||||
|
||||
//! Opens a file using the native assets manager and maps standard c file i/o
|
||||
//! \param filename The full path to the file
|
||||
//! \param mode Access mode of the file to be opened, will ignore write operations
|
||||
//! \param size[out] Returns the size of the file in bytes
|
||||
//! \return A standard c file handle
|
||||
static FILE* Open(const char* filename, const char* mode, AZ::u64& size);
|
||||
|
||||
//! Reads \p size bytes of a given open file. Is mapped to fread when a file is opened
|
||||
//! \param asset Raw pointer to the AAsset
|
||||
//! \param buffer Data blob to read into
|
||||
//! \param size Number of bytes to read. When called from fread redirect, value could be ignored in favor of
|
||||
//! using the internal cached version to ensure we are reading only the necessary number of bytes,
|
||||
//! otherwise we would be reading more than necessary as the redirected seems to only pass in 1024.
|
||||
//! \return The number of bytes read, zero on EOF, or < 0 on error.
|
||||
static int Read(void* asset, char* buffer, int size);
|
||||
|
||||
//! Writing to files inside an APK is unsupported. Is mapped to fwrite when a file is opened in order to correctly
|
||||
//! return an access error.
|
||||
//! \return EACCES
|
||||
static int Write(void* asset, const char* buffer, int size);
|
||||
|
||||
//! Same as, and is mapped to fseek when a file is opened.
|
||||
static fpos_t Seek(void* asset, fpos_t offset, int origin);
|
||||
|
||||
//! Closes the file handle and frees it's allocated resources. Is mapped to fclose when a file is opened.
|
||||
//! \return 0
|
||||
static int Close(void* asset);
|
||||
|
||||
//! Get the size, in bytes, of a file
|
||||
//! \param filename The full path to the file
|
||||
//! \return The size of the file, in bytes. Returns
|
||||
static int FileLength(const char* filename);
|
||||
|
||||
//! Uses JNI to cache the contents of a given directory at \p path while providing each entry to \p findCallback
|
||||
//! \param path The full path to the desired directory
|
||||
//! \param findCallback Callback used to find a specific file within a given directory
|
||||
//! \return If the directory was already cached \ref AZ::IO::ResultCode::Success if the directory was already cached or
|
||||
static AZ::IO::Result ParseDirectory(const char* path, FindDirsCallbackType findCallback);
|
||||
|
||||
//! Check to see if a given path is a directory or not
|
||||
static bool IsDirectory(const char* path);
|
||||
|
||||
//! Checks to see if a path (file or directory) exists
|
||||
static bool DirectoryOrFileExists(const char* path);
|
||||
|
||||
//! Set the correct number of bytes to be read when calls to fread are redirected to \ref APKFileHandler::Read
|
||||
static void SetNumBytesToRead(const size_t numBytesToRead);
|
||||
|
||||
//! Set the names of the files that should be loaded to memory
|
||||
static void SetLoadFilesToMemory(const char* fileNames);
|
||||
|
||||
|
||||
APKFileHandler();
|
||||
~APKFileHandler();
|
||||
|
||||
|
||||
private:
|
||||
MemoryBuffer* GetInMemoryFileBuffer(void* asset);
|
||||
void RemoveInMemoryFileBuffer(void* asset);
|
||||
bool ShouldLoadFileToMemory(const char* filePath);
|
||||
|
||||
typedef JNI::Internal::Object<AZ::OSAllocator> JniObject;
|
||||
|
||||
typedef AZ::OSStdAllocator StdAllocatorType;
|
||||
typedef AZ::OSString StringType;
|
||||
|
||||
typedef AZStd::vector<StringType, StdAllocatorType> StringVector;
|
||||
typedef AZStd::unordered_map<StringType, StringVector, AZStd::hash<StringType>, AZStd::equal_to<StringType>, StdAllocatorType> DirectoryCache;
|
||||
|
||||
|
||||
//! Internal accessor to the global APKFileHandler instance
|
||||
static APKFileHandler& Get();
|
||||
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(APKFileHandler);
|
||||
|
||||
|
||||
bool Initialize();
|
||||
bool IsReady() const;
|
||||
|
||||
|
||||
static AZ::EnvironmentVariable<APKFileHandler> s_instance; //!< Reference to the global APK file handler object, created in the AndroidEnv
|
||||
|
||||
AZStd::vector<MemoryBuffer> m_memFileBuffers;
|
||||
AZStd::vector<AZStd::string> m_memFileNames;
|
||||
|
||||
|
||||
AZStd::unique_ptr<JniObject> m_javaInstance; //!< JNI instance of the com.amazon.lumberyard.io.APKHandler Java object
|
||||
DirectoryCache m_cachedDirectories; //!< Cache of directories and their respective files already found through previous JNI calls
|
||||
size_t m_numBytesToRead; //!< Temp cache of the correct number of bytes to read when fread is called on an asset
|
||||
};
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
@@ -1,416 +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/Debug/Trace.h>
|
||||
|
||||
#include <AzCore/Android/AndroidEnv.h>
|
||||
#include <AzCore/Android/APKFileHandler.h>
|
||||
#include <AzCore/Android/JNI/Object.h>
|
||||
|
||||
#include <android/configuration.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
static const char* s_loadClassMethodName = "loadClass";
|
||||
|
||||
pthread_key_t AndroidEnv::s_jniEnvKey;
|
||||
AZ::EnvironmentVariable<AndroidEnv*> AndroidEnv::s_instance;
|
||||
|
||||
|
||||
// ----
|
||||
// AndroidEnv (public)
|
||||
// ----
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// static
|
||||
AndroidEnv* AndroidEnv::Get()
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::FindVariable<AndroidEnv*>(AZ::AzTypeInfo<AndroidEnv>::Name());
|
||||
AZ_Assert(s_instance, "The Android environment is NOT ready for use! Call Create first!");
|
||||
}
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// static
|
||||
bool AndroidEnv::Create(const Descriptor& descriptor)
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<AndroidEnv*>(AZ::AzTypeInfo<AndroidEnv>::Name());
|
||||
(*s_instance) = aznew AndroidEnv();
|
||||
}
|
||||
|
||||
if ((*s_instance)->IsReady()) // already created in a different module
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return (*s_instance)->Initialize(descriptor);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// static
|
||||
void AndroidEnv::Destroy()
|
||||
{
|
||||
if (s_instance)
|
||||
{
|
||||
if (s_instance.IsOwner())
|
||||
{
|
||||
(*s_instance)->Cleanup();
|
||||
delete (*s_instance);
|
||||
}
|
||||
s_instance.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "The Android environment is NOT ready for use! Call Create first!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
JNIEnv* AndroidEnv::GetJniEnv() const
|
||||
{
|
||||
JNIEnv* jniEnv = static_cast<JNIEnv*>(pthread_getspecific(s_jniEnvKey));
|
||||
if (!jniEnv)
|
||||
{
|
||||
jint status = m_jvm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6);
|
||||
if (status == JNI_EDETACHED)
|
||||
{
|
||||
AZ_TracePrintf("AndroidEnv", "JNI Env not attached to the VM");
|
||||
if (m_jvm->AttachCurrentThread(&jniEnv, NULL) != JNI_OK)
|
||||
{
|
||||
AZ_Assert(false, "Failed to attach tread to the JVM");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
pthread_setspecific(s_jniEnvKey, jniEnv);
|
||||
}
|
||||
return jniEnv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* AndroidEnv::GetObbFileName(bool mainFile) const
|
||||
{
|
||||
return (mainFile ? m_mainObbFileName.c_str() : m_patchObbFileName.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void AndroidEnv::UpdateConfiguration()
|
||||
{
|
||||
if (m_ownsConfiguration)
|
||||
{
|
||||
AConfiguration_fromAssetManager(m_configuration, m_assetManager);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
jclass AndroidEnv::LoadClass(const char *classPath)
|
||||
{
|
||||
JNIEnv* jniEnv = GetJniEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jstring classString = jniEnv->NewStringUTF(classPath);
|
||||
if (!classString || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to convert cstring %s to jstring", classPath);
|
||||
jniEnv->ExceptionDescribe();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jclass returnClass = m_classLoader->InvokeObjectMethod<jclass>(s_loadClassMethodName, classString);
|
||||
jniEnv->DeleteLocalRef(classString);
|
||||
|
||||
return returnClass;
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
// AndroidEnv (private)
|
||||
// ----
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// static
|
||||
void AndroidEnv::DestroyJniEnv(void *threadData)
|
||||
{
|
||||
JNIEnv* jniEnv = static_cast<JNIEnv*>(threadData);
|
||||
if (jniEnv)
|
||||
{
|
||||
JavaVM *javaVm = nullptr;
|
||||
jniEnv->GetJavaVM(&javaVm);
|
||||
javaVm->DetachCurrentThread();
|
||||
pthread_setspecific(s_jniEnvKey, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AndroidEnv::AndroidEnv()
|
||||
: m_jvm(nullptr)
|
||||
|
||||
, m_activityRef(nullptr)
|
||||
, m_activityClass(nullptr)
|
||||
|
||||
, m_classLoader()
|
||||
|
||||
, m_getClassNameMethod(nullptr)
|
||||
, m_getSimpleClassNameMethod(nullptr)
|
||||
|
||||
, m_assetManager(nullptr)
|
||||
, m_configuration(nullptr)
|
||||
, m_window(nullptr)
|
||||
|
||||
, m_appPrivateStoragePath()
|
||||
, m_appPublicStoragePath()
|
||||
, m_obbStoragePath()
|
||||
|
||||
, m_mainObbFileName()
|
||||
, m_patchObbFileName()
|
||||
|
||||
, m_packageName()
|
||||
, m_appVersionCode(0)
|
||||
|
||||
, m_ownsActivityRef(false)
|
||||
, m_ownsConfiguration(false)
|
||||
, m_isReady(false)
|
||||
|
||||
, m_isRunning(false)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AndroidEnv::~AndroidEnv()
|
||||
{
|
||||
if (s_instance)
|
||||
{
|
||||
AZ_Assert(s_instance.IsOwner(), "The Android Environment instance is being destroyed by someone other than the owner.");
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
bool AndroidEnv::Initialize(const Descriptor& descriptor)
|
||||
{
|
||||
m_jvm = descriptor.m_jvm;
|
||||
m_assetManager = descriptor.m_assetManager;
|
||||
m_configuration = descriptor.m_configuration;
|
||||
m_appPrivateStoragePath = descriptor.m_appPrivateStoragePath;
|
||||
m_appPublicStoragePath = descriptor.m_appPublicStoragePath;
|
||||
m_obbStoragePath = descriptor.m_obbStoragePath;
|
||||
|
||||
if (!m_configuration)
|
||||
{
|
||||
m_configuration = AConfiguration_new();
|
||||
AConfiguration_fromAssetManager(m_configuration, m_assetManager);
|
||||
m_ownsConfiguration = true;
|
||||
}
|
||||
|
||||
int result = pthread_key_create(&s_jniEnvKey, DestroyJniEnv);
|
||||
if (result)
|
||||
{
|
||||
AZ_Assert(false, "Something went wrong calling pthread_key_create... Error code: %d", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
JNIEnv* jniEnv = GetJniEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to get JNIEnv* on thread to initialize the AndroidEnv instance");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadClassNameMethods(jniEnv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
jobjectRefType refType = jniEnv->GetObjectRefType(descriptor.m_activityRef);
|
||||
if (refType == JNIGlobalRefType)
|
||||
{
|
||||
m_activityRef = descriptor.m_activityRef;
|
||||
}
|
||||
else if (refType == JNILocalRefType)
|
||||
{
|
||||
m_activityRef = static_cast<jclass>(jniEnv->NewGlobalRef(descriptor.m_activityRef));
|
||||
if (!m_activityRef || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to construct a global reference to the activity instance");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return false;
|
||||
}
|
||||
m_ownsActivityRef = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Unable to use 'activityRef' argument for global ref construction");
|
||||
return false;
|
||||
}
|
||||
|
||||
jclass activityClass = jniEnv->GetObjectClass(m_activityRef);
|
||||
|
||||
m_activityClass = static_cast<jclass>(jniEnv->NewGlobalRef(activityClass));
|
||||
if (!m_activityClass || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to construct a global reference to the activity class");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
jniEnv->DeleteLocalRef(activityClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
jniEnv->DeleteLocalRef(activityClass);
|
||||
|
||||
if (!CacheActivityData(jniEnv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_obbStoragePath.empty())
|
||||
{
|
||||
AZ::OSString relPath = AZ::OSString::format("/data/%s/files", m_packageName.c_str());
|
||||
AZ_Assert(m_appPublicStoragePath.find(relPath) != AZ::OSString::npos,
|
||||
"Public application storage path appears to be invalid. The OBB path may be incorrect and lead to unexpected results.");
|
||||
|
||||
AZ::OSString publicAndroidRoot = m_appPublicStoragePath.substr(0, m_appPublicStoragePath.length() - relPath.length());
|
||||
m_obbStoragePath = AZ::OSString::format("%s/obb/%s", publicAndroidRoot.c_str(), m_packageName.c_str());
|
||||
}
|
||||
|
||||
m_mainObbFileName = AZ::OSString::format("main.%d.%s.obb", m_appVersionCode, m_packageName.c_str());
|
||||
m_patchObbFileName = AZ::OSString::format("patch.%d.%s.obb", m_appVersionCode, m_packageName.c_str());
|
||||
|
||||
AZ_TracePrintf("AndroidEnv", "Application private storage path = %s", m_appPrivateStoragePath.c_str());
|
||||
AZ_TracePrintf("AndroidEnv", "Application public storage path = %s", m_appPublicStoragePath.c_str());
|
||||
AZ_TracePrintf("AndroidEnv", "Application OBB path = %s", m_obbStoragePath.c_str());
|
||||
|
||||
AZ_TracePrintf("AndroidEnv", "Main OBB file name = %s", m_mainObbFileName.c_str());
|
||||
AZ_TracePrintf("AndroidEnv", "Patch OBB file name = %s", m_patchObbFileName.c_str());
|
||||
|
||||
if (!APKFileHandler::Create())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to construct the global APK file handler");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_isReady = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void AndroidEnv::Cleanup()
|
||||
{
|
||||
if (m_ownsActivityRef)
|
||||
{
|
||||
JNI::DeleteRef(m_activityRef);
|
||||
}
|
||||
JNI::DeleteRef(m_activityClass);
|
||||
|
||||
if (m_ownsConfiguration)
|
||||
{
|
||||
AConfiguration_delete(m_configuration);
|
||||
}
|
||||
|
||||
APKFileHandler::Destroy();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
bool AndroidEnv::LoadClassNameMethods(JNIEnv* jniEnv)
|
||||
{
|
||||
const char* javaClassPath = "java/lang/Class";
|
||||
const char* getNameMethodName = "getName";
|
||||
const char* getSimpleNameMethodName = "getSimpleName";
|
||||
const char* getNameMethodSignature = "()Ljava/lang/String;";
|
||||
|
||||
// since we are requesting a system class, it should be safe to use FindClass instead
|
||||
// of the ClassLoader.
|
||||
jclass javaClass = jniEnv->FindClass(javaClassPath);
|
||||
if (!javaClass || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to find class %s from the JNI environment", javaClassPath);
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_getClassNameMethod = jniEnv->GetMethodID(javaClass, getNameMethodName, getNameMethodSignature);
|
||||
if (!m_getClassNameMethod || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to find method %s with signature %s in class %s", getNameMethodName, getNameMethodSignature, javaClassPath);
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
jniEnv->DeleteLocalRef(javaClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_getSimpleClassNameMethod = jniEnv->GetMethodID(javaClass, getSimpleNameMethodName, getNameMethodSignature);
|
||||
if (!m_getSimpleClassNameMethod || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to find method %s with signature %s in class %s", getSimpleNameMethodName, getNameMethodSignature, javaClassPath);
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
jniEnv->DeleteLocalRef(javaClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
jniEnv->DeleteLocalRef(javaClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
bool AndroidEnv::CacheActivityData(JNIEnv* jniEnv)
|
||||
{
|
||||
JniObject activityObject(m_activityClass, m_activityRef);
|
||||
|
||||
activityObject.RegisterMethod("GetPackageName", "()Ljava/lang/String;");
|
||||
activityObject.RegisterMethod("GetAppVersionCode", "()I");
|
||||
activityObject.RegisterMethod("getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
|
||||
m_packageName = activityObject.InvokeStringMethod("GetPackageName");
|
||||
m_appVersionCode = activityObject.InvokeIntMethod("GetAppVersionCode");
|
||||
|
||||
// construct the global class loader object
|
||||
jobject classLoaderRef = activityObject.InvokeObjectMethod<jobject>("getClassLoader");
|
||||
if (!classLoaderRef)
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to retrieve the class loader from the activity");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return false;
|
||||
}
|
||||
|
||||
jclass localClassLoaderClass = jniEnv->GetObjectClass(classLoaderRef);
|
||||
if (!localClassLoaderClass || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to get jclass from ClassLoader");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return false;
|
||||
}
|
||||
|
||||
jclass classLoaderClass = static_cast<jclass>(jniEnv->NewGlobalRef(localClassLoaderClass));
|
||||
if (!classLoaderClass || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AndroidEnv", false, "Failed to create a global reference to the class loader");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
jniEnv->DeleteLocalRef(localClassLoaderClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
jniEnv->DeleteLocalRef(localClassLoaderClass);
|
||||
|
||||
m_classLoader.reset(aznew JniObject(classLoaderClass, classLoaderRef, true));
|
||||
m_classLoader->RegisterMethod(s_loadClassMethodName, "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,218 +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/Module/Environment.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
struct AAssetManager;
|
||||
struct ANativeWindow;
|
||||
struct AConfiguration;
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template<typename Allocator>
|
||||
class Object;
|
||||
|
||||
template<typename StringType>
|
||||
class ClassName;
|
||||
} // namespace Internal
|
||||
} // namespace JNI
|
||||
|
||||
|
||||
class AndroidEnv
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(AndroidEnv, "{E51A8876-7A26-4CB1-BA88-394A128728C7}")
|
||||
AZ_CLASS_ALLOCATOR(AndroidEnv, AZ::OSAllocator, 0);
|
||||
|
||||
|
||||
//! Creation POD for the AndroidEnv
|
||||
struct Descriptor
|
||||
{
|
||||
Descriptor()
|
||||
: m_jvm(nullptr)
|
||||
, m_activityRef(nullptr)
|
||||
, m_assetManager(nullptr)
|
||||
, m_configuration(nullptr)
|
||||
, m_appPrivateStoragePath()
|
||||
, m_appPublicStoragePath()
|
||||
, m_obbStoragePath()
|
||||
{
|
||||
}
|
||||
|
||||
JavaVM* m_jvm; //!< Global pointer to the Java virtual machine
|
||||
jobject m_activityRef; //!< Local or global reference to the activity instance
|
||||
AAssetManager* m_assetManager; //!< Global pointer to the Android asset manager, used for APK file i/o
|
||||
AConfiguration* m_configuration; //!< Global pointer to the configuration of the device, e.g. orientation, screen density, locale, etc.
|
||||
AZ::OSString m_appPrivateStoragePath; //!< Access restricted location. E.G. /data/data/<package_name>/files
|
||||
AZ::OSString m_appPublicStoragePath; //!< Public storage specifically for the application. E.G. <public_storage>/Android/data/<package_name>/files
|
||||
AZ::OSString m_obbStoragePath; //!< Public storage specifically for the application's obb files. E.G. <public_storage>/Android/obb/<package_name>/files
|
||||
};
|
||||
|
||||
//! Public accessor to the global AndroidEnv instance
|
||||
static AndroidEnv* Get();
|
||||
|
||||
//! The preferred entry point for the construction of the global AndroidEnv instance
|
||||
static bool Create(const Descriptor& descriptor);
|
||||
|
||||
//! Public accessor to destroy the AndroidEnv global instance
|
||||
static void Destroy();
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
//! Request a thread specific JNIEnv pointer from the JVM.
|
||||
//! \return A pointer to the JNIEnv on the current thread.
|
||||
JNIEnv* GetJniEnv() const;
|
||||
|
||||
//! Request the global reference to the activity class
|
||||
jclass GetActivityClassRef() const { return m_activityClass; }
|
||||
|
||||
//! Request the global reference to the activity instance
|
||||
jobject GetActivityRef() const { return m_activityRef; }
|
||||
|
||||
//! Get the global pointer to the Android asset manager, which is used for APK file i/o.
|
||||
AAssetManager* GetAssetManager() const { return m_assetManager; }
|
||||
|
||||
//! Get the global pointer to the device/application configuration,
|
||||
AConfiguration* GetConfiguration() const { return m_configuration; }
|
||||
|
||||
//! Set the global pointer to the Android window surface.
|
||||
void SetWindow(ANativeWindow* window) { m_window = window; }
|
||||
|
||||
//! Get the global pointer to the Android window surface
|
||||
ANativeWindow* GetWindow() const { return m_window; }
|
||||
|
||||
//! Get the hidden internal storage, typically this is where the application is installed on the device.
|
||||
//! e.g. /data/data/<package_name/files
|
||||
const char* GetAppPrivateStoragePath() const { return m_appPrivateStoragePath.c_str(); }
|
||||
|
||||
//! Get the application specific directory for public storage.
|
||||
//! e.g. <public_storage>/Android/data/<package_name/files
|
||||
const char* GetAppPublicStoragePath() const { return m_appPublicStoragePath.c_str(); }
|
||||
|
||||
//! Get the application specific directory for obb files.
|
||||
//! e.g. <public_storage>/Android/obb/<package_name/files
|
||||
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
|
||||
const char* GetPackageName() const { return m_packageName.c_str(); }
|
||||
|
||||
//! Get the app version code (android:versionCode in the manifest).
|
||||
int GetAppVersionCode() const { return m_appVersionCode; }
|
||||
|
||||
//! Get the filename of the obb. This doesn't include the path to the obb folder.
|
||||
const char* GetObbFileName(bool mainFile) const;
|
||||
|
||||
//! Check if the AndroidEnv has been initialized
|
||||
bool IsReady() const { return m_isReady; }
|
||||
|
||||
//! Set wheather or not the application should be running
|
||||
void SetIsRunning(bool isRunning) { m_isRunning = isRunning; }
|
||||
|
||||
//! Check if the application has been backgrounded (false) or not (true)
|
||||
bool IsRunning() const { return m_isRunning; }
|
||||
|
||||
//! If the AndroidEnv owns the native configuration, it will be updated with the latest configuration
|
||||
//! information, otherwise nothing will happen.
|
||||
void UpdateConfiguration();
|
||||
|
||||
//! Loads a Java class as opposed to attempting to find a loaded class from the call stack.
|
||||
//! \param classPath The fully qualified forward slash separated Java class path.
|
||||
//! \return A global reference to the desired jclass. Caller is responsible for making a
|
||||
//! call to DeleteGlobalJniRef when the jclass is no longer needed.
|
||||
jclass LoadClass(const char* classPath);
|
||||
private:
|
||||
template<typename StringType>
|
||||
friend class JNI::Internal::ClassName;
|
||||
|
||||
typedef JNI::Internal::Object<OSAllocator> JniObject; //!< Internal usage of \ref AZ::Android::JNI::Internal::Object that uses the OSAllocator
|
||||
|
||||
|
||||
//! Callback for when a thread exists to detach the jni env from the thread
|
||||
//! \param threadData Expected to be the JNIEnv pointer
|
||||
static void DestroyJniEnv(void* threadData);
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
AndroidEnv();
|
||||
~AndroidEnv();
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(AndroidEnv);
|
||||
|
||||
//! Public global accessor to the android application environment
|
||||
//! \param descriptor
|
||||
bool Initialize(const Descriptor& descriptor);
|
||||
|
||||
//! Handle the deletion of the global jni references
|
||||
void Cleanup();
|
||||
|
||||
//! Finds the java/lang/Class jclass to get the method IDs to getName and getSimpleName
|
||||
//! \return True if successfully, False otherwise
|
||||
bool LoadClassNameMethods(JNIEnv* jniEnv);
|
||||
|
||||
//! Calls some java methods on the activity instance and constructs the class loader
|
||||
//! \return True if successfully, False otherwise
|
||||
bool CacheActivityData(JNIEnv* jniEnv);
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
static pthread_key_t s_jniEnvKey; //!< Thread key for accessing the thread specific jni env pointers
|
||||
static AZ::EnvironmentVariable<AndroidEnv*> s_instance; //!< Reference to the global object, created in the main function (AndroidLauncher)
|
||||
|
||||
|
||||
JavaVM* m_jvm; //!< Mostly used for [de/a]ttaching JNIEnv pointers to threads
|
||||
|
||||
jobject m_activityRef; //!< Reference to the global instance of the current activity object, used for instance method invocation, field access
|
||||
jclass m_activityClass; //!< Reference to the global instance of the current activity class, used for method / field extraction, static method invocation
|
||||
|
||||
AZStd::unique_ptr<JniObject> m_classLoader; //!< Class loader instance, used for finding Java classes on any thread
|
||||
|
||||
jmethodID m_getClassNameMethod; //!< Method ID for getName from java/lang/Class which returns a fully qualified dot separated Java class path
|
||||
jmethodID m_getSimpleClassNameMethod; //!< Method ID for getSimpleName from java/lang/Class which returns just the class name from a Java class path
|
||||
|
||||
AAssetManager* m_assetManager; //!< Global pointer to the Android asset manager, used for APK file i/o
|
||||
AConfiguration* m_configuration; //!< Global pointer to the configuration of the device, e.g. orientation, screen density, locale, etc.
|
||||
ANativeWindow* m_window; //!< Global pointer to the window surface created by Android, used for creating GL contexts
|
||||
|
||||
AZ::OSString m_appPrivateStoragePath; //!< Access restricted location. E.G. /data/data/<package_name>/files
|
||||
AZ::OSString m_appPublicStoragePath; //!< Public storage specifically for the application. E.G. <public_storage>/Android/data/<package_name>/files
|
||||
AZ::OSString m_obbStoragePath; //!< Public storage specifically for the application's obb files. E.G. <public_storage>/Android/obb/<package_name>/files
|
||||
|
||||
AZ::OSString m_mainObbFileName; //!< File name for the main OBB
|
||||
AZ::OSString m_patchObbFileName; //!< File name for the patch OBB
|
||||
|
||||
AZ::OSString m_packageName; //!< The dot separated package id of the application
|
||||
int m_appVersionCode; //!< The version code of the app (android:versionCode in the AndroidManifest.xml)
|
||||
|
||||
bool m_ownsActivityRef; //!< For when a local activity ref is passed into the construction and needs to be cleaned up
|
||||
bool m_ownsConfiguration; //!< For when no configuration is passed into the construction and needs to be cleaned up
|
||||
bool m_isReady; //!< Set only once the object has been successfully constructed
|
||||
|
||||
bool m_isRunning; //!< Internal flag indicating if the application is running, mainly used to determine if we shoudl be blocking on the event pump while paused
|
||||
};
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
@@ -1,85 +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 <android/api-level.h>
|
||||
|
||||
#include <AzCore/Android/Utils.h>
|
||||
|
||||
|
||||
// the following defines provide cross compatibility between NDK and header versions as they
|
||||
// were only officially added to the unified headers in NDK r14
|
||||
#ifndef __ANDROID_API_K__
|
||||
#define __ANDROID_API_K__ 19
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_L__
|
||||
#define __ANDROID_API_L__ 21
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_L_MR1__
|
||||
#define __ANDROID_API_L_MR1__ 22
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_M__
|
||||
#define __ANDROID_API_M__ 23
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_N__
|
||||
#define __ANDROID_API_N__ 24
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_N_MR1__
|
||||
#define __ANDROID_API_N_MR1__ 25
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_O__
|
||||
#define __ANDROID_API_O__ 26
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_O_MR1__
|
||||
#define __ANDROID_API_O_MR1__ 27
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_P__
|
||||
#define __ANDROID_API_P__ 28
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID_API_Q__
|
||||
#define __ANDROID_API_Q__ 29
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
//! Supported API level codes for runtime checks
|
||||
enum class ApiLevel : unsigned char
|
||||
{
|
||||
KitKat = __ANDROID_API_K__,
|
||||
Lollipop = __ANDROID_API_L__,
|
||||
Lollipop_mr1 = __ANDROID_API_L_MR1__,
|
||||
Marshmallow = __ANDROID_API_M__,
|
||||
Nougat = __ANDROID_API_N__,
|
||||
Nougat_mr1 = __ANDROID_API_N_MR1__,
|
||||
Oreo = __ANDROID_API_O__,
|
||||
Oreo_mr1 = __ANDROID_API_O_MR1__,
|
||||
Pie = __ANDROID_API_P__,
|
||||
Ten = __ANDROID_API_Q__,
|
||||
};
|
||||
|
||||
//! Request the OS runtime API level of the device
|
||||
AZ_INLINE ApiLevel GetRuntimeApiLevel()
|
||||
{
|
||||
AConfiguration* config = Utils::GetConfiguration();
|
||||
ApiLevel sdkVersion = static_cast<ApiLevel>(AConfiguration_getSdkVersion(config));
|
||||
AZ_Assert(sdkVersion >= ApiLevel::KitKat, "The Android runtime API level detected (%d) is unsupported", sdkVersion);
|
||||
return sdkVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +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/Trace.h>
|
||||
|
||||
#include <AzCore/Android/AndroidEnv.h>
|
||||
#include <AzCore/Android/JNI/Internal/JStringUtils.h>
|
||||
|
||||
|
||||
namespace AZ { namespace Android { namespace JNI { namespace Internal
|
||||
{
|
||||
//! \brief Utility for getting the Java class names
|
||||
//! \tparam StringType The type of string that should be return during generation. Defaults to AZStd::string
|
||||
template<typename StringType = AZStd::string>
|
||||
class ClassName
|
||||
{
|
||||
public:
|
||||
//! Get the fully qualified forward slash separated Java class path of Java class ref.
|
||||
//! e.g. android.app.NativeActivity ==> android/app/NativeActivity
|
||||
//! \param classRef A valid reference to a java class
|
||||
//! \return A copy of the class name
|
||||
static StringType GetName(jclass classRef)
|
||||
{
|
||||
StringType className;
|
||||
|
||||
AndroidEnv* androidEnv = AndroidEnv::Get();
|
||||
AZ_Assert(androidEnv, "Attempting to use the AndroidEnv before it's created");
|
||||
|
||||
if (androidEnv)
|
||||
{
|
||||
className = GetNameImpl(classRef, androidEnv->m_getClassNameMethod);
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
//! Get just the name of the Java class from a Java class ref.
|
||||
//! e.g. android.app.NativeActivity ==> NativeActivity
|
||||
//! \param classRef A valid reference to a java class
|
||||
//! \return A copy of the class name
|
||||
static StringType GetSimpleName(jclass classRef)
|
||||
{
|
||||
StringType className;
|
||||
|
||||
AndroidEnv* androidEnv = AndroidEnv::Get();
|
||||
AZ_Assert(androidEnv, "Attempting to use the AndroidEnv before it's created");
|
||||
|
||||
if (androidEnv)
|
||||
{
|
||||
className = GetNameImpl(classRef, androidEnv->m_getSimpleClassNameMethod);
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static StringType GetNameImpl(jclass classRef, jmethodID methodId)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
AZ_Error("JNI::ClassName", false, "Failed to get JNIEnv* on thread on call to GetClassNameImpl");
|
||||
return StringType();
|
||||
}
|
||||
|
||||
jstring rawStringValue = static_cast<jstring>(jniEnv->CallObjectMethod(classRef, methodId));
|
||||
if (!rawStringValue || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("JNI::ClassName", false, "Failed to invoke a GetName variant method on class Unknown");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return StringType();
|
||||
}
|
||||
|
||||
StringType className = ConvertJstringToStringImpl<StringType>(rawStringValue);
|
||||
jniEnv->DeleteLocalRef(rawStringValue);
|
||||
|
||||
return className;
|
||||
}
|
||||
};
|
||||
} // namespace Internal
|
||||
} // namespace JNI
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
@@ -1,33 +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 <jni.h>
|
||||
|
||||
|
||||
namespace AZ { namespace Android { namespace JNI { namespace Internal
|
||||
{
|
||||
//! Converts a jstring to a string type
|
||||
//! \param stringValue A local or global reference to a jstring object
|
||||
//! \return A copy of the converted string
|
||||
template<typename StringType>
|
||||
StringType ConvertJstringToStringImpl(jstring stringValue);
|
||||
|
||||
//! Converts a string to a jstring
|
||||
//! \param stringValue The native string value to be converted
|
||||
//! \return A global reference to the converted jstring. The caller is responsible for
|
||||
//! deleting it when no longer needed
|
||||
template<typename StringType>
|
||||
jstring ConvertStringToJstringImpl(const StringType& stringValue);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace JNI
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
|
||||
#include <AzCore/Android/JNI/Internal/JStringUtils_impl.h>
|
||||
@@ -1,79 +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/Debug/Trace.h>
|
||||
#include <AzCore/Android/JNI/JNI.h>
|
||||
|
||||
|
||||
namespace AZ { namespace Android { namespace JNI { namespace Internal
|
||||
{
|
||||
//! Converts a jstring to a string type
|
||||
//! \param stringValue A local or global reference to a jstring object
|
||||
//! \return A copy of the converted string
|
||||
template<typename StringType>
|
||||
StringType ConvertJstringToStringImpl(jstring stringValue)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
AZ_Error("AZ::Android::JNI", false, "Failed to get JNIEnv* on thread for jstring conversion");
|
||||
return StringType();
|
||||
}
|
||||
|
||||
const char* convertedStringValue = jniEnv->GetStringUTFChars(stringValue, nullptr);
|
||||
if (!convertedStringValue || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AZ::Android::JNI", false, "Failed to convert a jstring to cstring");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return StringType();
|
||||
}
|
||||
|
||||
StringType localCopy(convertedStringValue);
|
||||
jniEnv->ReleaseStringUTFChars(stringValue, convertedStringValue);
|
||||
|
||||
return localCopy;
|
||||
}
|
||||
|
||||
//! Converts a string to a jstring
|
||||
//! \param stringValue The native string value to be converted
|
||||
//! \return A global reference to the converted jstring. The caller is responsible for
|
||||
//! deleting it when no longer needed
|
||||
template<typename StringType>
|
||||
AZ_INLINE jstring ConvertStringToJstringImpl(const StringType& stringValue)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
if (!jniEnv)
|
||||
{
|
||||
AZ_Error("AZ::Android::JNI", false, "Failed to get JNIEnv* on thread for jstring conversion");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jstring localRef = jniEnv->NewStringUTF(stringValue.c_str());
|
||||
if (!localRef || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AZ::Android::JNI", false, "Failed to convert the cstring to jstring");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jstring globalRef = static_cast<jstring>(jniEnv->NewGlobalRef(localRef));
|
||||
if (!globalRef || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("AZ::Android::JNI", false, "Failed to create a global reference to the return jstring");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
jniEnv->DeleteLocalRef(localRef);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jniEnv->DeleteLocalRef(localRef);
|
||||
|
||||
return globalRef;
|
||||
}
|
||||
} // namespace Internal
|
||||
} // namespace JNI
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +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/Debug/Trace.h>
|
||||
#include <AzCore/Android/JNI/Internal/ClassName.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template<typename StringType>
|
||||
StringType GetTypeSignature(jobject value)
|
||||
{
|
||||
StringType signature("");
|
||||
|
||||
AZ_Error("JNI::Signature", value, "Call to GetTypeSignature with null jobject");
|
||||
if (value)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
AZ_Assert(jniEnv, "Failed to get JNIEnv* on thread for get signature call");
|
||||
|
||||
if (jniEnv)
|
||||
{
|
||||
jclass objectClass = jniEnv->GetObjectClass(value);
|
||||
|
||||
StringType typeSig = ClassName<StringType>::GetName(objectClass);
|
||||
signature.reserve(typeSig.size() + 3);
|
||||
|
||||
signature.append("L");
|
||||
signature.append(typeSig);
|
||||
signature.append(";");
|
||||
|
||||
jniEnv->DeleteLocalRef(objectClass);
|
||||
|
||||
AZStd::replace(signature.begin(), signature.end(), '.', '/');
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
template<typename StringType>
|
||||
StringType GetTypeSignature(jobjectArray value)
|
||||
{
|
||||
StringType signature("");
|
||||
|
||||
AZ_Error("JNI::Signature", value, "Call to GetTypeSignature with null jobjectArray");
|
||||
if (value)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
AZ_Assert(jniEnv, "Failed to get JNIEnv* on thread for get signature call");
|
||||
|
||||
if (jniEnv)
|
||||
{
|
||||
jobject element = jniEnv->GetObjectArrayElement(value, 0);
|
||||
if (!element || jniEnv->ExceptionCheck())
|
||||
{
|
||||
AZ_Error("JNI::Signature", false, "Unable to determine jobject array type");
|
||||
HANDLE_JNI_EXCEPTION(jniEnv);
|
||||
}
|
||||
else
|
||||
{
|
||||
signature.append("[");
|
||||
signature.append(GetTypeSignature<StringType>(element));
|
||||
|
||||
jniEnv->DeleteLocalRef(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
||||
template<typename StringType, typename Type>
|
||||
bool CompareTypeSignature(const StringType& baseSignature, Type param)
|
||||
{
|
||||
return (baseSignature.compare(GetTypeSignature(param)) == 0);
|
||||
}
|
||||
|
||||
template<typename StringType>
|
||||
bool CompareTypeSignature(const StringType& baseSignature, jobject param)
|
||||
{
|
||||
bool result = (!baseSignature.empty() && param);
|
||||
if (result)
|
||||
{
|
||||
// check if the baseSignature is malformed e.g. doesn't start with 'L' or end with ';'
|
||||
if (baseSignature[0] != 'L' || baseSignature[baseSignature.length() - 1] != ';')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// strip the preceding 'L' and trailing ';' from the class path
|
||||
StringType classPath = baseSignature.substr(1, baseSignature.length() - 2);
|
||||
|
||||
if (JNIEnv* jniEnv = GetEnv())
|
||||
{
|
||||
// since it's valid to pass a derived java class through JNI we will need
|
||||
// to check if the argument is an instance of the specified signature to
|
||||
// accurately validate the signature
|
||||
jclass signatureClass = LoadClass(classPath.c_str());
|
||||
if (!signatureClass)
|
||||
{
|
||||
AZ_Assert(false, "Unable to load class in signature %s", classPath.c_str());
|
||||
return false;
|
||||
}
|
||||
result = (jniEnv->IsInstanceOf(param, signatureClass) == JNI_TRUE);
|
||||
DeleteRef(signatureClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename StringType>
|
||||
bool CompareTypeSignature(const StringType& baseSignature, jobjectArray param)
|
||||
{
|
||||
bool result = (!baseSignature.empty() && param);
|
||||
if (result)
|
||||
{
|
||||
// check if the baseSignature is malformed e.g. doesn't start with '['
|
||||
if (baseSignature[0] != '[')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// strip the preceding '['
|
||||
StringType typeSignature = baseSignature.substr(1, baseSignature.length() - 1);
|
||||
|
||||
if (JNIEnv* jniEnv = GetEnv())
|
||||
{
|
||||
jobject javaObject = jniEnv->GetObjectArrayElement(static_cast<jobjectArray>(param), 0);
|
||||
result = CompareTypeSignature(typeSignature, javaObject);
|
||||
DeleteRef(javaObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace Internal
|
||||
|
||||
|
||||
template<typename StringType>
|
||||
template<typename Type, typename... Args>
|
||||
bool Signature<StringType>::ValidateImpl(Type firstParam, Args&&... parameters)
|
||||
{
|
||||
const char* signature = m_signature.c_str();
|
||||
const char* currentSignature = &(signature[m_currentIndex]);
|
||||
|
||||
int paramLength;
|
||||
|
||||
// extract the fully qualified class path for java objects
|
||||
if (currentSignature[0] == 'L' || (strncmp(currentSignature, "[L", 2) == 0))
|
||||
{
|
||||
int endIndex = m_signature.find(';', m_currentIndex);
|
||||
if (endIndex == StringType::npos)
|
||||
{
|
||||
AZ_Assert(false, "The base signature supplied (%s) for validation is malformed", m_signature.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
paramLength = (endIndex - m_currentIndex) + 1; // +1 to include the trailing semicolon
|
||||
}
|
||||
// otherwise just extract the primitive type char(s)
|
||||
else
|
||||
{
|
||||
paramLength = ((currentSignature[0] == '[') ? 2 : 1); // primitive types are 1 character signatures, arrays are 2
|
||||
}
|
||||
|
||||
// extract the parameter signature and compare the value
|
||||
StringType paramSignature = m_signature.substr(m_currentIndex, paramLength);
|
||||
if (!Internal::CompareTypeSignature(paramSignature, firstParam))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentIndex = m_currentIndex + paramLength;
|
||||
if (m_currentIndex >= m_signatureLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ValidateImpl(AZStd::forward<Args>(parameters)...);
|
||||
}
|
||||
} // namespace JNI
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
@@ -1,105 +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/Android/JNI/JNI.h>
|
||||
#include <AzCore/Android/JNI/Internal/JStringUtils.h>
|
||||
#include <AzCore/Android/JNI/Internal/ClassName.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
////////////////////////////////////////////////////////////////
|
||||
JNIEnv* GetEnv()
|
||||
{
|
||||
return AndroidEnv::Get()->GetJniEnv();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
jclass LoadClass(const char* classPath)
|
||||
{
|
||||
return AndroidEnv::Get()->LoadClass(classPath);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AZStd::string GetClassName(jclass classRef)
|
||||
{
|
||||
return Internal::ClassName<AZStd::string>::GetName(classRef);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AZStd::string GetSimpleClassName(jclass classRef)
|
||||
{
|
||||
return Internal::ClassName<AZStd::string>::GetSimpleName(classRef);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AZStd::string ConvertJstringToString(jstring stringValue)
|
||||
{
|
||||
return Internal::ConvertJstringToStringImpl<AZStd::string>(stringValue);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
jstring ConvertStringToJstring(const AZStd::string& stringValue)
|
||||
{
|
||||
return Internal::ConvertStringToJstringImpl(stringValue);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
int GetRefType(jobject javaRef)
|
||||
{
|
||||
int refType = JNIInvalidRefType;
|
||||
|
||||
if (javaRef)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
AZ_Assert(jniEnv, "Unable to get JNIEnv pointer to determine JNI reference type.");
|
||||
|
||||
if (jniEnv)
|
||||
{
|
||||
refType = jniEnv->GetObjectRefType(javaRef);
|
||||
}
|
||||
}
|
||||
|
||||
return refType;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void DeleteRef(jobject javaRef)
|
||||
{
|
||||
if (javaRef)
|
||||
{
|
||||
JNIEnv* jniEnv = GetEnv();
|
||||
AZ_Assert(jniEnv, "Unable to get JNIEnv pointer to free JNI reference.");
|
||||
|
||||
if (jniEnv)
|
||||
{
|
||||
jobjectRefType refType = jniEnv->GetObjectRefType(javaRef);
|
||||
switch (refType)
|
||||
{
|
||||
case JNIGlobalRefType:
|
||||
jniEnv->DeleteGlobalRef(javaRef);
|
||||
break;
|
||||
case JNILocalRefType:
|
||||
jniEnv->DeleteLocalRef(javaRef);
|
||||
break;
|
||||
case JNIWeakGlobalRefType:
|
||||
jniEnv->DeleteWeakGlobalRef(javaRef);
|
||||
break;
|
||||
default:
|
||||
AZ_Error("AZ::Android::JNI", false, "Unknown or invalid reference type detected.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +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/string/string.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/asset_manager.h>
|
||||
|
||||
|
||||
#define HANDLE_JNI_EXCEPTION(jniEnv) \
|
||||
jniEnv->ExceptionDescribe(); \
|
||||
jniEnv->ExceptionClear();
|
||||
|
||||
|
||||
#if defined(AZ_DEBUG_BUILD)
|
||||
#define JNI_SIGNATURE_VALIDATION
|
||||
#endif
|
||||
|
||||
|
||||
// redefine the JNI_FALSE and JNI_TRUE macros to ensure their correct types are represented when using them
|
||||
#if defined(JNI_FALSE)
|
||||
#undef JNI_FALSE
|
||||
#define JNI_FALSE jboolean(0)
|
||||
#endif // defined(JNI_FALSE)
|
||||
|
||||
#if defined(JNI_TRUE)
|
||||
#undef JNI_TRUE
|
||||
#define JNI_TRUE jboolean(1)
|
||||
#endif // defined(JNI_TRUE)
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
//! Request a thread specific JNIEnv pointer from the Android environment.
|
||||
//! \return A pointer to the JNIEnv on the current thread.
|
||||
JNIEnv* GetEnv();
|
||||
|
||||
//! Loads a Java class as opposed to attempting to find a loaded class from the call stack.
|
||||
//! \param classPath The fully qualified forward slash separated Java class path.
|
||||
//! \return A global reference to the desired jclass. Caller is responsible for making a
|
||||
//! call do DeleteGlobalJniRef when the jclass is no longer needed.
|
||||
jclass LoadClass(const char* classPath);
|
||||
|
||||
//! Get the fully qualified forward slash separated Java class path of Java class ref.
|
||||
//! e.g. android.app.NativeActivity ==> android/app/NativeActivity
|
||||
//! \param classRef A valid reference to a java class
|
||||
//! \return A copy of the class name
|
||||
AZStd::string GetClassName(jclass classRef);
|
||||
|
||||
//! Get just the name of the Java class from a Java class ref.
|
||||
//! e.g. android.app.NativeActivity ==> NativeActivity
|
||||
//! \param classRef A valid reference to a java class
|
||||
//! \return A copy of the class name
|
||||
AZStd::string GetSimpleClassName(jclass classRef);
|
||||
|
||||
//! Converts a jstring to a AZStd::string
|
||||
//! \param stringValue A local or global reference to a jstring object
|
||||
//! \return A copy of the converted string
|
||||
AZStd::string ConvertJstringToString(jstring stringValue);
|
||||
|
||||
//! Converts a string to a jstring
|
||||
//! \param stringValue The native string value to be converted
|
||||
//! \return A global reference to the converted jstring. The caller is responsible for
|
||||
//! deleting it when no longer needed
|
||||
jstring ConvertStringToJstring(const AZStd::string& stringValue);
|
||||
|
||||
//! Gets the reference type of the Java object. Can be Local, Global or Weak Global.
|
||||
//! \param javaRef Raw Java object reference, can be null.
|
||||
//! \return The result of GetObjectRefType as long as the object is valid,
|
||||
//! otherwise JNIInvalidRefType.
|
||||
int GetRefType(jobject javaRef);
|
||||
|
||||
//! Deletes a JNI object/class reference. Will handle local, global and weak global references.
|
||||
//! \param javaRef Raw java object reference.
|
||||
void DeleteRef(jobject javaRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,484 +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/SystemAllocator.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
#include <AzCore/std/typetraits/is_convertible.h>
|
||||
#include <AzCore/std/typetraits/conditional.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
|
||||
#include <AzCore/Android/JNI/JNI.h>
|
||||
|
||||
#if defined(JNI_SIGNATURE_VALIDATION)
|
||||
#include <AzCore/Android/JNI/Signature.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace AZ { namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
//! Utility to allow easier managing of JNI reference when hosting Java classes and objects
|
||||
//! in native code. Provides the same functionality that is available when manipulating JNI
|
||||
//! references directly with the raw JNIEnv pointer.
|
||||
//! \tparam Allocator The type of allocator used for both it self and all it's internal
|
||||
//! allocations. Defaults to AZ::SystemAllocator
|
||||
template<typename Allocator = AZ::SystemAllocator>
|
||||
class Object final
|
||||
{
|
||||
private:
|
||||
//! special case if we are using the SystemAllocator to use the default AZStd::allocator instead of wrapping
|
||||
//! the allocator in AZStdAlloc. This way the known string types (AZStd::string and AZ::OSString) are correctly
|
||||
//! typedefed internally.
|
||||
typedef typename AZStd::conditional<AZStd::is_same<Allocator, AZ::SystemAllocator>::value, AZStd::allocator, AZStdAlloc<Allocator>>::type AZStdAllocator;
|
||||
|
||||
|
||||
public:
|
||||
typedef AZStd::basic_string<char, AZStd::char_traits<char>, AZStdAllocator> string_type;
|
||||
typedef AZStd::vector<JNINativeMethod, AZStdAllocator> vector_type;
|
||||
|
||||
|
||||
AZ_CLASS_ALLOCATOR(Object<Allocator>, Allocator, 0);
|
||||
|
||||
|
||||
//! Creates a custom jni object wrapper from a java class path. This JNI object
|
||||
//! will take owner ship of all global refs used internally
|
||||
//! \param classPath The full java class path for the object to be loaded
|
||||
//! \param className The name of the java class, mostly use for logging purposes
|
||||
explicit Object(const char* classPath, const char* className = nullptr);
|
||||
|
||||
//! Creates a JNI object wrapper based on an existing global ref
|
||||
//! \param classRef The global reference to the jclass for the object
|
||||
//! \param objectRef The global reference to the instance object
|
||||
//! \param takeOwnership [Optional] Tell the object to clean up the argument global refs when destroyed
|
||||
Object(jclass classRef, jobject objectRef, bool takeOwnership = false);
|
||||
|
||||
//! Automatically cleans up any global JNI reference with the JVM
|
||||
~Object();
|
||||
|
||||
|
||||
//! Register a non-static Java method with the associated Java object instance. These methods can only
|
||||
//! be invoked with a valid jobject reference.
|
||||
//! \param methodName The exact name of the java method to register
|
||||
//! \param methodSignature The argument/return signature of the java method.
|
||||
//! See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp16432 for more details
|
||||
//! \return True if the method was registered successfully, False otherwise
|
||||
bool RegisterMethod(const char* methodName, const char* methodSignature);
|
||||
|
||||
//! Register a static Java method with the associated Java class reference. These methods
|
||||
//! can be invoked as long as the class reference is valid.
|
||||
//! \param methodName The exact name of the java method to register
|
||||
//! \param methodSignature The argument/return signature of the Java method.
|
||||
//! See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp16432 for more details
|
||||
//! \return True if the method was registered successfully, False otherwise
|
||||
bool RegisterStaticMethod(const char* methodName, const char* methodSignature);
|
||||
|
||||
//! Register native callback with Java 'native' method.
|
||||
//! \param nativeMethods All the native methods to register with the Java class.
|
||||
//! \return True if all the methods were registered successfully, False otherwise
|
||||
bool RegisterNativeMethods(vector_type nativeMethods);
|
||||
|
||||
//! Register a instance member field with the object.
|
||||
//! \param fieldName The exact name of the java field to register.
|
||||
//! \param fieldSignature The type signature of the Java field.
|
||||
//! See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp16432 for more details
|
||||
//! \return True if the field was registered successfully, False otherwise.
|
||||
bool RegisterField(const char* fieldName, const char* fieldSignature);
|
||||
|
||||
//! Register a static member field with the object.
|
||||
//! \param fieldName The exact name of the java static field to register.
|
||||
//! \param fieldSignature The type signature of the Java static field.
|
||||
//! See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp16432 for more details
|
||||
//! \return True if the static field was registered successfully, False otherwise.
|
||||
bool RegisterStaticField(const char* fieldName, const char* fieldSignature);
|
||||
|
||||
|
||||
//! Creates a global reference to an java instance object
|
||||
//! \param constructorSignature The function signature of the constructor desired for creating the object
|
||||
//! See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp16432 for more details
|
||||
//! \param parameters All the arguments required for the function call
|
||||
//! \return True if the global instance was created successfully, False otherwise
|
||||
template<typename... Args>
|
||||
bool CreateInstance(const char* constructorSignature, Args&&... parameters);
|
||||
|
||||
//! Destroys the global instance of the java object only. Static method calls can still be made, while instance methods will
|
||||
//! fail until a new instance is constructed through CreateInstance
|
||||
void DestroyInstance();
|
||||
|
||||
|
||||
//!@{
|
||||
//! All the Invoke<TYPE>Method functions are for calling registered instance methods on
|
||||
//! a java object where <TYPE> is the return type of the java method.
|
||||
//! \param methodName The exact name of the java method to call
|
||||
//! \param parameters All the arguments required for the function call
|
||||
template<typename... Args>
|
||||
void InvokeVoidMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jboolean InvokeBooleanMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jbyte InvokeByteMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jchar InvokeCharMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jshort InvokeShortMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jint InvokeIntMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jlong InvokeLongMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jfloat InvokeFloatMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jdouble InvokeDoubleMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
string_type InvokeStringMethod(const char* methodName, Args&&... parameters);
|
||||
//!@}
|
||||
|
||||
//! Call a java instance method that returns a customs java object such as an String, Array
|
||||
//! or other java class type. This function is restricted to types derived from _jobject.
|
||||
//! The return value will be a global reference and the caller is responsible for deleting
|
||||
//! through Jni::DeleteGlobalRef when no longer needed.
|
||||
template<typename ReturnType, typename... Args>
|
||||
typename AZStd::enable_if<AZStd::is_convertible<ReturnType, jobject>::value, ReturnType>::type
|
||||
InvokeObjectMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
|
||||
//!@{
|
||||
//! All the InvokeStatic<TYPE>Method functions are for calling registered static methods on
|
||||
//! a java object where <TYPE> is the return type of the java method.
|
||||
//! \param methodName The exact name of the java method to call
|
||||
//! \param parameters All the arguments required for the function call
|
||||
template<typename... Args>
|
||||
void InvokeStaticVoidMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jboolean InvokeStaticBooleanMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jbyte InvokeStaticByteMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jchar InvokeStaticCharMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jshort InvokeStaticShortMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jint InvokeStaticIntMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jlong InvokeStaticLongMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jfloat InvokeStaticFloatMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
jdouble InvokeStaticDoubleMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
template<typename... Args>
|
||||
string_type InvokeStaticStringMethod(const char* methodName, Args&&... parameters);
|
||||
//!@}
|
||||
|
||||
//! Call a java static method that returns a customs java object such as an String, Array or
|
||||
//! other java class type. This function is restricted to types derived from _jobject.
|
||||
//! The return value will be a global reference and the caller is responsible for deleting
|
||||
//! through Jni::DeleteGlobalRef when no longer needed.
|
||||
template<typename ReturnType, typename... Args>
|
||||
typename AZStd::enable_if<AZStd::is_convertible<ReturnType, jobject>::value, ReturnType>::type
|
||||
InvokeStaticObjectMethod(const char* methodName, Args&&... parameters);
|
||||
|
||||
|
||||
//!@{
|
||||
//! All the Set<TYPE>Field functions are for setting a registered instance member on
|
||||
//! a java object where <TYPE> is the type of the instance member.
|
||||
//! \param fieldName The exact name of the java instance member to set.
|
||||
//! \param value The new value to set the instance member.
|
||||
void SetBooleanField(const char* fieldName, jboolean value);
|
||||
void SetByteField(const char* fieldName, jbyte value);
|
||||
void SetCharField(const char* fieldName, jchar value);
|
||||
void SetShortField(const char* fieldName, jshort value);
|
||||
void SetIntField(const char* fieldName, jint value);
|
||||
void SetLongField(const char* fieldName, jlong value);
|
||||
void SetFloatField(const char* fieldName, jfloat value);
|
||||
void SetDoubleField(const char* fieldName, jdouble value);
|
||||
void SetStringField(const char* fieldName, const string_type& value);
|
||||
//!@}
|
||||
|
||||
//! Set a custom java object instance field. Restricted to types derived from _jobject,
|
||||
//! such as jstring, jarray, etc.
|
||||
template<typename ValueType, typename... Args>
|
||||
typename AZStd::enable_if<AZStd::is_convertible<ValueType, jobject>::value>::type
|
||||
SetObjectField(const char* fieldName, ValueType value);
|
||||
|
||||
//!@{
|
||||
//! All the Get<TYPE>Field functions are for getting a registered instance member on
|
||||
//! a java object where <TYPE> is the type of the instance member.
|
||||
//! \param fieldName The exact name of the java instance member to get.
|
||||
jboolean GetBooleanField(const char* fieldName);
|
||||
jbyte GetByteField(const char* fieldName);
|
||||
jchar GetCharField(const char* fieldName);
|
||||
jshort GetShortField(const char* fieldName);
|
||||
jint GetIntField(const char* fieldName);
|
||||
jlong GetLongField(const char* fieldName);
|
||||
jfloat GetFloatField(const char* fieldName);
|
||||
jdouble GetDoubleField(const char* fieldName);
|
||||
string_type GetStringField(const char* fieldName);
|
||||
//!@}
|
||||
|
||||
//! Get a custom java object instance field. Restricted to types derived from _jobject,
|
||||
//! such as jstring, jarray, etc. A global refernece will be returned and the caller is
|
||||
//! responsible for deleting through Jni::DeleteGlobalRef when no longer needed.
|
||||
template<typename ReturnType, typename... Args>
|
||||
typename AZStd::enable_if<AZStd::is_convertible<ReturnType, jobject>::value, ReturnType>::type
|
||||
GetObjectField(const char* fieldName);
|
||||
|
||||
|
||||
//!@{
|
||||
//! All the Set<TYPE>Field functions are for setting a registered instance member on
|
||||
//! a java object where <TYPE> is the type of the instance member.
|
||||
//! \param fieldName The exact name of the java instance member to set.
|
||||
//! \param value The new value to set the static member.
|
||||
void SetStaticBooleanField(const char* fieldName, jboolean value);
|
||||
void SetStaticByteField(const char* fieldName, jbyte value);
|
||||
void SetStaticCharField(const char* fieldName, jchar value);
|
||||
void SetStaticShortField(const char* fieldName, jshort value);
|
||||
void SetStaticIntField(const char* fieldName, jint value);
|
||||
void SetStaticLongField(const char* fieldName, jlong value);
|
||||
void SetStaticFloatField(const char* fieldName, jfloat value);
|
||||
void SetStaticDoubleField(const char* fieldName, jdouble value);
|
||||
void SetStaticStringField(const char* fieldName, const string_type& value);
|
||||
//!@}
|
||||
|
||||
//! Set a custom java object static field. Restricted to types derived from _jobject,
|
||||
//! such as jstring, jarray, etc.
|
||||
template<typename ValueType, typename... Args>
|
||||
typename AZStd::enable_if<AZStd::is_convertible<ValueType, jobject>::value>::type
|
||||
SetStaticObjectField(const char* fieldName, ValueType value);
|
||||
|
||||
|
||||
//!@{
|
||||
//! All the Get<TYPE>Field functions are for getting a registered static member on
|
||||
//! a java object where <TYPE> is the type of the static member.
|
||||
//! \param fieldName The exact name of the java instance member to get.
|
||||
jboolean GetStaticBooleanField(const char* fieldName);
|
||||
jbyte GetStaticByteField(const char* fieldName);
|
||||
jchar GetStaticCharField(const char* fieldName);
|
||||
jshort GetStaticShortField(const char* fieldName);
|
||||
jint GetStaticIntField(const char* fieldName);
|
||||
jlong GetStaticLongField(const char* fieldName);
|
||||
jfloat GetStaticFloatField(const char* fieldName);
|
||||
jdouble GetStaticDoubleField(const char* fieldName);
|
||||
string_type GetStaticStringField(const char* fieldName);
|
||||
//!@}
|
||||
|
||||
//! Get a custom java object static field. Restricted to types derived from _jobject,
|
||||
//! such as jstring, jarray, etc. A global reference will be returned and the caller is
|
||||
//! responsible for deleting through Jni::DeleteGlobalRef when no longer needed.
|
||||
template<typename ReturnType, typename... Args>
|
||||
typename AZStd::enable_if<AZStd::is_convertible<ReturnType, jobject>::value, ReturnType>::type
|
||||
GetStaticObjectField(const char* fieldName);
|
||||
|
||||
|
||||
private:
|
||||
//! Simple structure containing core information about a registered Java method
|
||||
struct JMethodCache
|
||||
{
|
||||
jmethodID m_methodId; //!< Pointer to the method reference on the JVM
|
||||
|
||||
#if defined(JNI_SIGNATURE_VALIDATION)
|
||||
string_type m_methodName; //!< Name of the Java method, mostly used for debug logs
|
||||
string_type m_argumentSignature; //!< Java type signature of all method arguments e.g. (int, String) => ILjava/lang/String;
|
||||
string_type m_returnSignature; //!< Java type signature of the return value
|
||||
#endif
|
||||
};
|
||||
|
||||
//! Simple structure containing core information about a registered Java field
|
||||
struct JFieldCache
|
||||
{
|
||||
jfieldID m_fieldId; //!< Pointer to the field reference on the JVM
|
||||
|
||||
#if defined(JNI_SIGNATURE_VALIDATION)
|
||||
string_type m_fieldName; //!< Name of the Java field, mostly used for debug logs
|
||||
string_type m_signature; //!< Java type signature of the field
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
typedef AZStd::shared_ptr<JMethodCache> JMethodCachePtr;
|
||||
typedef AZStd::shared_ptr<JFieldCache> JFieldCachePtr;
|
||||
|
||||
template<typename ValueType>
|
||||
using CacheMap = AZStd::unordered_map<string_type, ValueType, AZStd::hash<string_type>, AZStd::equal_to<string_type>, AZStdAllocator>;
|
||||
|
||||
typedef CacheMap<JMethodCachePtr> JMethodMap;
|
||||
typedef CacheMap<JFieldCachePtr> JFieldMap;
|
||||
|
||||
|
||||
template<typename ReturnType>
|
||||
using JniMethodCallback = AZStd::function<ReturnType(JNIEnv*, jobject, jmethodID)>;
|
||||
|
||||
template<typename ReturnType>
|
||||
using JniStaticMethodCallback = AZStd::function<ReturnType(JNIEnv*, jclass, jmethodID)>;
|
||||
|
||||
template<typename ReturnType>
|
||||
using JniFieldCallback = AZStd::function<ReturnType(JNIEnv*, jobject, jfieldID)>;
|
||||
|
||||
template<typename ReturnType>
|
||||
using JniStaticFieldCallback = AZStd::function<ReturnType(JNIEnv*, jclass, jfieldID)>;
|
||||
|
||||
|
||||
#if defined(JNI_SIGNATURE_VALIDATION)
|
||||
using SignatureOutcome = AZ::Outcome<void, string_type>;
|
||||
|
||||
typedef Signature<string_type> SigUtil;
|
||||
#endif
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
//! Helper to find a register instance method
|
||||
JMethodCachePtr GetMethod(const string_type& methodName) const;
|
||||
|
||||
//! Helper to find a register static method
|
||||
JMethodCachePtr GetStaticMethod(const string_type& methodName) const;
|
||||
|
||||
//! Helper to find a register instance field
|
||||
JFieldCachePtr GetField(const string_type& fieldName) const;
|
||||
|
||||
//! Helper to find a register static field
|
||||
JFieldCachePtr GetStaticField(const string_type& fieldName) const;
|
||||
|
||||
|
||||
#if defined(JNI_SIGNATURE_VALIDATION)
|
||||
//! Helper to extract the argument and return signatures from a complete method signature
|
||||
//! and set the respective properties within the specified JMethodCache pointer.
|
||||
//! \param methodCache JMethodCache pointer to set
|
||||
//! \param methodName The exact name of the java method
|
||||
//! \param methodSignature The full method signature
|
||||
void SetMethodSignature(JMethodCachePtr methodCache, const char* methodName, const char* methodSignature);
|
||||
|
||||
//! Helper to extract the argument and return signatures from a complete method signature
|
||||
//! and set the respective properties within the specified JMethodCache pointer.
|
||||
//! \param fieldCache JFieldCache pointer to set
|
||||
//! \param fieldName The exact name of the java field
|
||||
//! \param signature The signature, or type, of the java field
|
||||
void SetFieldSignature(JFieldCachePtr fieldCache, const char* fieldName, const char* signature);
|
||||
|
||||
//! Performs a full signature validation for all types in Args
|
||||
//! \param baseSignature The base signature used to register the JNI method or field
|
||||
//! \param parameters Pending arguments to a JNI call needing validation
|
||||
//! \return \ref AZ::Success if all parameters pass validation, \ref AZ::Failure<string_type> containing the
|
||||
//! error message otherwise
|
||||
template<typename... Args>
|
||||
SignatureOutcome ValidateSignature(const string_type& baseSignature, Args&&... parameters);
|
||||
|
||||
//! Performs a partial signature validation when \p Type is jobject or jobjectArray, and a full signature validation
|
||||
//! for other types. Primarily used for basic validation of return type values when the requested type
|
||||
//! can't be deduced without making the JNI call first.
|
||||
//! \param baseSignature The base signature used to register the JNI method or field
|
||||
//! \param param Pending type to a JNI call needing validation
|
||||
//! \return \ref AZ::Success if all parameters pass validation, \ref AZ::Failure<string_type> containing the
|
||||
//! error message otherwise
|
||||
template<typename Type>
|
||||
SignatureOutcome ValidateSignaturePartial(const string_type& baseSignature, Type param);
|
||||
#endif // defined(JNI_SIGNATURE_VALIDATION)
|
||||
|
||||
|
||||
//! Helper for invoking a primitive type instance method on the JNI object
|
||||
//! \param methodName The name of the instance method to invoke
|
||||
//! \param jniCallback Lambda wrapper to the actual JNIEnv::Call<type>Method
|
||||
//! \param parameters Java method call arguments list
|
||||
//! \return The primitive return value from the Java method
|
||||
template<typename ReturnType, typename... Args>
|
||||
ReturnType InvokePrimitiveTypeMethodInternal(const char* methodName, JniMethodCallback<ReturnType> jniCallback, Args&&... parameters);
|
||||
|
||||
//! Helper for invoking a primitive type instance method on the JNI object
|
||||
//! \param methodName The name of the instance method to invoke
|
||||
//! \param jniCallback Lambda wrapper to the actual JNIEnv::CallStatic<type>Method
|
||||
//! \param parameters Java method call arguments list
|
||||
//! \return The primitive return value from the Java method
|
||||
template<typename ReturnType, typename... Args>
|
||||
ReturnType InvokePrimitiveTypeStaticMethodInternal(const char* methodName, JniStaticMethodCallback<ReturnType> jniCallback, Args&&... parameters);
|
||||
|
||||
|
||||
//! Helper for setting a primitive type instance field
|
||||
//! \param fieldName The name of the instance field to set
|
||||
//! \param jniCallback Lambda wrapper to the actual JNIEnv::Set<type>Field
|
||||
//! \param value New value of the instance field
|
||||
template<typename ValueType>
|
||||
void SetPrimitiveTypeFieldInternal(const char* fieldName, JniFieldCallback<void> jniCallback, ValueType value);
|
||||
|
||||
//! Helper for getting a primitive type instance field
|
||||
//! \param fieldName The name of the instance field to set
|
||||
//! \param jniCallback Lambda wrapper to the actual JNIEnv::Get<type>Field
|
||||
//! \return The current value of the primitive instance field
|
||||
template<typename ReturnType>
|
||||
ReturnType GetPrimitiveTypeFieldInternal(const char* fieldName, JniFieldCallback<ReturnType> jniCallback);
|
||||
|
||||
|
||||
//! Helper for setting a primitive type static field
|
||||
//! \param fieldName The name of the static field to set
|
||||
//! \param jniCallback Lambda wrapper to the actual JNIEnv::SetStatic<type>Field
|
||||
//! \param value New value of the static field
|
||||
template<typename ValueType>
|
||||
void SetPrimitiveTypeStaticFieldInternal(const char* fieldName, JniStaticFieldCallback<void> jniCallback, ValueType value);
|
||||
|
||||
//! Helper for getting a primitive type static field
|
||||
//! \param fieldName The name of the static field to set
|
||||
//! \param jniCallback Lambda wrapper to the actual JNIEnv::GetStatic<type>Field
|
||||
//! \return The current value of the primitive static field
|
||||
template<typename ReturnType>
|
||||
ReturnType GetPrimitiveTypeStaticFieldInternal(const char* fieldName, JniStaticFieldCallback<ReturnType> jniCallback);
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
string_type m_className; //!< The simple name of the Java class, used for debugging
|
||||
|
||||
AZStdAllocator m_stdAllocator; //!< Allocator instance used for allocating the JMethodCachePtr/JFieldCachePtr shared pointers
|
||||
|
||||
jclass m_classRef; //!< A global reference to the java class, used for method/filed extraction, static method invocation
|
||||
jobject m_objectRef; //!< A global reference to the java object instance, used for instance method invocation, field access
|
||||
|
||||
JMethodMap m_methods; //!< Container of all the instance methods currently registered for the java class
|
||||
JMethodMap m_staticMethods; //!< Container of all the static methods currently registered for the java class
|
||||
|
||||
JFieldMap m_fields; //!< Container of all the instance fields currently registered for the java class
|
||||
JFieldMap m_staticFields; //!< Container of all the static fields currently registered for the java class
|
||||
|
||||
bool m_ownsGlobalRefs; //!< Should the global references be destroyed automatically or manually
|
||||
bool m_instanceConstructed; //!< Can we invoke instance methods, has CreateInstance ben called
|
||||
};
|
||||
} // namespace Internal
|
||||
|
||||
//! \brief The default \ref AZ::Android::JNI::Internal::Object type which uses the SystemAllocator
|
||||
typedef Internal::Object<AZ::SystemAllocator> Object;
|
||||
} // namespace JNI
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
#include <AzCore/Android/JNI/Internal/Object_impl.h>
|
||||
@@ -1,29 +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/SystemAllocator.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
template<typename Allocator>
|
||||
class Object;
|
||||
}
|
||||
|
||||
//! \brief The default \ref AZ::Android::JNI::Internal::Object type which uses the SystemAllocator
|
||||
typedef Internal::Object<AZ::SystemAllocator> Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,269 +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/string/string.h>
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
|
||||
#include <AzCore/Android/JNI/JNI.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
//! \brief Templated interface for getting specific JNI type signatures. This is intentionally left empty
|
||||
//! to enforce usage to only known template specializations.
|
||||
//! \tparam Type The JNI type desired for the signature request
|
||||
//! \tparam StringType The type of string that should be returned. Defaults to 'const char*'
|
||||
//! \return String containing the type specific JNI signature
|
||||
template<typename Type, typename StringType = const char*>
|
||||
StringType GetTypeSignature(Type);
|
||||
|
||||
///!@{
|
||||
//! \brief Known type specializations for \ref AZ::Android::JNI::Internal::GetTypeSignature.
|
||||
template<> inline const char* GetTypeSignature(jboolean) { return "Z"; }
|
||||
template<> inline const char* GetTypeSignature(bool) { return "Z"; }
|
||||
template<> inline const char* GetTypeSignature(jbooleanArray) { return "[Z"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jbyte) { return "B"; }
|
||||
template<> inline const char* GetTypeSignature(jbyteArray) { return "[B"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jchar) { return "C"; }
|
||||
template<> inline const char* GetTypeSignature(jcharArray) { return "[C"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jshort) { return "S"; }
|
||||
template<> inline const char* GetTypeSignature(jshortArray) { return "[S"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jint) { return "I"; }
|
||||
template<> inline const char* GetTypeSignature(jintArray) { return "[I"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jlong) { return "J"; }
|
||||
template<> inline const char* GetTypeSignature(jlongArray) { return "[J"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jfloat) { return "F"; }
|
||||
template<> inline const char* GetTypeSignature(jfloatArray) { return "[F"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jdouble) { return "D"; }
|
||||
template<> inline const char* GetTypeSignature(jdoubleArray) { return "[D"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jstring) { return "Ljava/lang/String;"; }
|
||||
|
||||
template<> inline const char* GetTypeSignature(jclass) { return "Ljava/lang/Class;"; }
|
||||
|
||||
template<typename StringType>
|
||||
StringType GetTypeSignature(jobject value);
|
||||
|
||||
template<typename StringType>
|
||||
StringType GetTypeSignature(jobjectArray value);
|
||||
//!@}
|
||||
|
||||
|
||||
//! \brief Templated interface for comparing JNI type signatures. This leverages
|
||||
//! \ref AZ::Android::JNI::Internal::GetTypeSignature under the hood
|
||||
//! to weed out unsupported types
|
||||
//! \tparam StringType The type of string that should used for base comparison
|
||||
//! \tparam Type The JNI type desired for the signature verification
|
||||
//! \param baseSignature The string representation of the expected type \p param should be
|
||||
//! \param param The raw JNI type to be validated
|
||||
//! \return True if \p param is an acceptable type for the type specified in \p baseSignature,
|
||||
//! false otherwise
|
||||
template<typename StringType, typename Type>
|
||||
bool CompareTypeSignature(const StringType& baseSignature, Type param);
|
||||
|
||||
///!@{
|
||||
//! \brief Explicit definitions for jobject and jobjectArray need to be defined in order to support
|
||||
//! subclass validation for Java classes
|
||||
template<typename StringType>
|
||||
bool CompareTypeSignature(const StringType& baseSignature, jobject param);
|
||||
|
||||
template<typename StringType>
|
||||
bool CompareTypeSignature(const StringType& baseSignature, jobjectArray param);
|
||||
//!@}
|
||||
}
|
||||
|
||||
|
||||
//! \brief Utility for generating and validating JNI signatures
|
||||
//! \tparam StringType The type of string used internally for generation and validation. Defaults to AZStd::string
|
||||
template<typename StringType = AZStd::string>
|
||||
class Signature
|
||||
{
|
||||
public:
|
||||
//! \brief Required for handling cases when an empty set of variadic arguments are forwarded from
|
||||
//! \ref AZ::Android::JNI::GetSignature calls
|
||||
//! \return An empty string
|
||||
static StringType Generate()
|
||||
{
|
||||
Signature sig;
|
||||
return sig.m_signature;
|
||||
}
|
||||
|
||||
//! \brief Gets the signature from n-number of parameters
|
||||
//! \param parameters Variables only used to forward their types on to \ref AZ::Android::JNI::Signature::GenerateImpl
|
||||
//! \return String containing a fully qualified Java signature
|
||||
template<typename... Args>
|
||||
static StringType Generate(Args&&... parameters)
|
||||
{
|
||||
Signature sig;
|
||||
sig.GenerateImpl(AZStd::forward<Args>(parameters)...);
|
||||
return sig.m_signature;
|
||||
}
|
||||
|
||||
|
||||
//! \brief Required for handling cases when an empty set of variadic arguments are forwarded from
|
||||
//! \ref AZ::Android::JNI::ValidateSignature calls
|
||||
//! \param baseSignature The string representation of the expected type signature. Should be an empty string in
|
||||
//! this case.
|
||||
//! \return True if the string is empty (e.g. nothing to validate), false otherwise
|
||||
static bool Validate(const StringType& baseSignature)
|
||||
{
|
||||
Signature sig(baseSignature);
|
||||
return sig.m_signature.empty();
|
||||
}
|
||||
|
||||
//! \brief Validates n-number of parameters type signatures
|
||||
//! \param baseSignature The string representation of the expected JNI type signatures in \p parameters
|
||||
//! \param parameters The input arguments to be validated
|
||||
//! \return True if all arguments in \p parameters match the expected type signature in \p baseSignature, False otherwise
|
||||
template<typename... Args>
|
||||
static bool Validate(const StringType& baseSignature, Args&&... parameters)
|
||||
{
|
||||
Signature sig(baseSignature);
|
||||
return (sig.m_signature.empty() ?
|
||||
false :
|
||||
sig.ValidateImpl(AZStd::forward<Args>(parameters)...));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//! \brief Internal constructor of the signature util used for generation. Pre-allocates some memory for the internal
|
||||
//! cache to try and prevent the hammering of reallocations in most cases.
|
||||
Signature()
|
||||
: m_signature()
|
||||
, m_signatureLength(0)
|
||||
, m_currentIndex(0)
|
||||
{
|
||||
m_signature.reserve(8);
|
||||
}
|
||||
|
||||
//! \brief Internal constructor of the signature util used for validation.
|
||||
//! \param baseSignature The signature to compare against
|
||||
explicit Signature(const StringType& baseSignature)
|
||||
: m_signature(baseSignature)
|
||||
, m_signatureLength(0)
|
||||
, m_currentIndex(0)
|
||||
{
|
||||
m_signatureLength = m_signature.length();
|
||||
}
|
||||
|
||||
|
||||
//! \brief Appends the desired type to the internal signature cache
|
||||
//! \param value Throwaway variable only used to forward the type on to \ref AZ::Android::JNI::Internal::GetTypeSignature
|
||||
template<typename Type>
|
||||
void GenerateImpl(Type value)
|
||||
{
|
||||
m_signature.append(Internal::GetTypeSignature(value));
|
||||
}
|
||||
|
||||
///!@{
|
||||
//! \brief Explicit definitions for jobject and jobjectArray need to be defined in order to route their
|
||||
//! calls to the correct version of \ref AZ::Android::JNI::Internal::GetTypeSignature which returns
|
||||
//! a string instead of a c-string.
|
||||
void GenerateImpl(jobject value)
|
||||
{
|
||||
m_signature.append(Internal::GetTypeSignature<StringType>(value));
|
||||
}
|
||||
|
||||
void GenerateImpl(jobjectArray value)
|
||||
{
|
||||
m_signature.append(Internal::GetTypeSignature<StringType>(value));
|
||||
}
|
||||
//!@}
|
||||
|
||||
//! \brief Appends the desired type to the internal signature cache
|
||||
//! \param first Variable only used to forward the type on to \ref AZ::Android::JNI::Signature::GenerateImpl
|
||||
//! \param parameters Additional variables only used to forward their types into recursive calls
|
||||
template<typename Type, typename... Args>
|
||||
void GenerateImpl(Type first, Args&&... parameters)
|
||||
{
|
||||
GenerateImpl(first);
|
||||
GenerateImpl(AZStd::forward<Args>(parameters)...);
|
||||
}
|
||||
|
||||
|
||||
//! \brief Validates a single (or final) type against the remaining signature(s) in the internal signature cache
|
||||
//! \param param The type to be validated
|
||||
//! \return True if the type of \p param matches the remaining signature(s) in the internal signature cache,
|
||||
//! False otherwise
|
||||
template<typename Type>
|
||||
bool ValidateImpl(Type param)
|
||||
{
|
||||
int paramLength = m_signatureLength - m_currentIndex;
|
||||
if (paramLength > 0)
|
||||
{
|
||||
StringType paramSignature = m_signature.substr(m_currentIndex, paramLength);
|
||||
return Internal::CompareTypeSignature<StringType>(paramSignature, param);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! \brief Validates n-number of parameters type signatures
|
||||
//! \param first The first type to be validated
|
||||
//! \param parameters The remaining types to be validated recursively
|
||||
//! \return True if all the types in \p parameters match the expected type signature stored internally,
|
||||
//! False otherwise
|
||||
template<typename Type, typename... Args>
|
||||
bool ValidateImpl(Type first, Args&&... parameters);
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
StringType m_signature; //!< Internal cache for signature generation/validation
|
||||
|
||||
int m_signatureLength; //!< Cache of the total length of the base signature for validation
|
||||
int m_currentIndex; //!< Current index in walking the base signature for validation
|
||||
};
|
||||
|
||||
|
||||
//! \brief Default Signature template (AZStd::string), primarily used in \ref AZ::Android::JNI::GetSignature
|
||||
//! and \ref AZ::Android::JNI::ValidateSignature
|
||||
typedef Signature<AZStd::string> SignatureUtil;
|
||||
|
||||
|
||||
//! \brief Generates a fully qualified Java signature from n-number of parameters. This is the preferred implementation
|
||||
//! for generating JNI signatures
|
||||
//! \param parameters Variables only used to forward their type info on to \ref AZ::Android::Signature::Generate
|
||||
//! \return String containing a fully qualified Java signature
|
||||
template<typename... Args>
|
||||
AZ_INLINE AZStd::string GetSignature(Args&&... parameters)
|
||||
{
|
||||
return SignatureUtil::Generate(AZStd::forward<Args>(parameters)...);
|
||||
}
|
||||
|
||||
//! \brief Validates a JNI signature with n-number of parameters. Will walk the signature validating
|
||||
//! each parameter individually. The validation will exit once an argument fails validation.
|
||||
//! \param baseSignature Base JNI signature to be comparing against
|
||||
//! \param parameters The input arguments to be validated
|
||||
//! \return True if all arguments match the signature, False otherwise
|
||||
template<typename... Args>
|
||||
AZ_INLINE bool ValidateSignature(const AZStd::string& baseSignature, Args&&... parameters)
|
||||
{
|
||||
return SignatureUtil::Validate(baseSignature, AZStd::forward<Args>(parameters)...);
|
||||
};
|
||||
} // namespace JNI
|
||||
} // namespace Android
|
||||
} // namespace AZ
|
||||
|
||||
#include <AzCore/Android/JNI/Internal/Signature_impl.h>
|
||||
|
||||
|
||||
@@ -1,132 +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/base.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <AzCore/std/typetraits/typetraits.h>
|
||||
|
||||
#include <AzCore/Android/JNI/JNI.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
//! A scoped_ref works in the same way a AZStd::scoped_ptr except it's specificially
|
||||
//! designed for the opaque pointer JNI types (e.g. jobject, jarray, etc.). Guarantees
|
||||
//! the java object is released from the JNI environment when the scoped_ref falls
|
||||
//! out of scope.
|
||||
template<typename JniType>
|
||||
class scoped_ref
|
||||
{
|
||||
static_assert(AZStd::is_convertible<JniType, jobject>::value, "Specified type is not convertible to jobject.");
|
||||
|
||||
typedef scoped_ref<JniType> ThisType;
|
||||
|
||||
public:
|
||||
typedef JniType ThisType::* UnspecifiedBoolType;
|
||||
|
||||
|
||||
// ---
|
||||
|
||||
//! Only explicit scoped_refs are allowed to be constructed
|
||||
//! \param javaObject Raw pointer to the java object. Currently only supports Local and
|
||||
//! Global reference types. Weak Global reference are NOT supported.
|
||||
explicit scoped_ref(JniType javaObject = nullptr)
|
||||
: m_javaObject(javaObject)
|
||||
{
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
if (m_javaObject)
|
||||
{
|
||||
int refType = GetRefType(m_javaObject);
|
||||
AZ_Error("JNI::scoped_ref",
|
||||
refType == JNIGlobalRefType || refType == JNILocalRefType,
|
||||
"Unsupported JNI reference type (%d) used in JNI::scoped_ref. "
|
||||
"Supported reference types are JNIGlobalRefType and JNILocalRefType. "
|
||||
"This may lead to unexpected behaviour.", refType);
|
||||
}
|
||||
#endif // defined(AZ_ENABLE_TRACING)
|
||||
}
|
||||
|
||||
//! Automatically release the reference with the JNI environment when the object
|
||||
//! goes out of scope
|
||||
~scoped_ref()
|
||||
{
|
||||
DeleteRef(m_javaObject);
|
||||
}
|
||||
|
||||
//! Compatibilty with the 'not' operator for validity checks. Only checkes for raw
|
||||
//! pointer validity, NOT if it's pointing to a null reference (weak global ref).
|
||||
bool operator !() const
|
||||
{
|
||||
return (m_javaObject == nullptr);
|
||||
}
|
||||
|
||||
//! Operator for implicit bool conversions
|
||||
//! \return 'True' if internal reference is valid, False otherwise
|
||||
operator UnspecifiedBoolType() const
|
||||
{
|
||||
return (m_javaObject == nullptr ? nullptr : &ThisType::m_javaObject);
|
||||
}
|
||||
|
||||
//! Explicit accessor of the raw pointer to the java reference.
|
||||
//! \return The raw pointer to the java object reference
|
||||
JniType get() const
|
||||
{
|
||||
return m_javaObject;
|
||||
}
|
||||
|
||||
//! Swap the internal reference with another scoped_ref of the same type
|
||||
//! \param lhs The scoped_ref (of same type) to be swaped with
|
||||
void swap(scoped_ref& lhs)
|
||||
{
|
||||
AZStd::swap(m_javaObject, lhs.m_javaObject);
|
||||
}
|
||||
|
||||
//! Reset the internal reference with a new pointer
|
||||
//! \param javaObject Raw pointer to the java object. Must be of same type.
|
||||
void reset(JniType javaObject = nullptr)
|
||||
{
|
||||
// Pointer level self reset. Triggering this assert will cause a crash when either this
|
||||
// reference is used, or when the this scoped ref is cleaned up (double/invalid delete).
|
||||
AZ_Assert(javaObject == nullptr || javaObject != m_javaObject, "JNI::scoped_ref pointer level self reset!");
|
||||
|
||||
// JNI reference level "self" reset. The references themselves are different so this is a
|
||||
// valid reset, however the underlining java object the references are pointing to
|
||||
// is the same in this case. As far as the JNI environment is concerned this is ok
|
||||
// but we should still make note of these occurrences.
|
||||
// NOTE: This warning will also trigger in the event the pointers are the same.
|
||||
AZ_Warning("JNI::scoped_ref", GetEnv()->IsSameObject(m_javaObject, javaObject) == JNI_FALSE, "JNI::scoped_ref JNI reference level self reset.");
|
||||
|
||||
ThisType(javaObject).swap(*this);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//! Disable copy/move
|
||||
///@{
|
||||
AZ_DISABLE_COPY_MOVE(scoped_ref);
|
||||
///@}
|
||||
|
||||
//! Disable direct comparisons of other scoped_refs
|
||||
///@{
|
||||
void operator==(scoped_ref const&) const;
|
||||
void operator!=(scoped_ref const&) const;
|
||||
///@}
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
JniType m_javaObject; //!< Raw pointer of the java object reference (e.g. jobject, jarray, etc.)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,445 +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/Trace.h>
|
||||
#include <AzCore/std/typetraits/is_convertible.h>
|
||||
#include <AzCore/std/smart_ptr/shared_count.h>
|
||||
|
||||
#include <AzCore/Android/JNI/JNI.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace JNI
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
using sp_counted_base = AZStd::Internal::sp_counted_base;
|
||||
using sp_typeinfo = AZStd::type_id;
|
||||
|
||||
|
||||
//! Similar to the AZStd::Internal::sp_counted_impl_pa in that accepts the data type and
|
||||
//! custom allocator type, however the data type is restricted to types that inherit from
|
||||
//! jobject. See AzCore/std/smartptr/shared_count.h for more details
|
||||
template<typename JniType, typename AllocatorType>
|
||||
class sr_counted_impl
|
||||
: public AZStd::Internal::sp_counted_base
|
||||
{
|
||||
static_assert(AZStd::is_convertible<JniType, jobject>::value, "Specified type is not convertible to jobject.");
|
||||
|
||||
public:
|
||||
//! Only explicit contruction of the shared_count private impl for shared_refs
|
||||
//! \param javaObject The raw JNI pointer
|
||||
//! \param allocator Custom allocator, used in the deallocation of this particular object,
|
||||
//! NOT the JNI pointer
|
||||
sr_counted_impl(JniType javaObject, const AllocatorType& allocator)
|
||||
: m_javaObject(javaObject)
|
||||
, m_allocator(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
//! Called when the use count drops to zero. DOES release the JNI referene.
|
||||
void dispose() override
|
||||
{
|
||||
DeleteRef(m_javaObject);
|
||||
}
|
||||
|
||||
//! Called when the weak count drops to zero. Does NOT release the JNI referene.
|
||||
void destroy() override
|
||||
{
|
||||
this->~ThisType();
|
||||
m_allocator.deallocate(this, sizeof(ThisType), AZStd::alignment_of<ThisType>::value);
|
||||
}
|
||||
|
||||
//! Throwaway pure-virtual. Custom deleters are not supported for shared_refs since
|
||||
//! they have to be released from the JNI environment
|
||||
void* get_deleter(sp_typeinfo const&) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
typedef sr_counted_impl<JniType, AllocatorType> ThisType;
|
||||
|
||||
//! Disable copy/move
|
||||
///@{
|
||||
AZ_DISABLE_COPY_MOVE(sr_counted_impl);
|
||||
///@}
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
JniType m_javaObject; //!< The raw JNI pointer from the JVM
|
||||
AllocatorType m_allocator; //!< Custom alloctor used for the [de]allocation of this object
|
||||
};
|
||||
|
||||
//! Similar to the AZStd::Internal::shared_count, however the data type is restricted to
|
||||
//! types that inherit from jobject. See AzCore/std/smartptr/shared_count.h for more details
|
||||
class shared_count
|
||||
{
|
||||
public:
|
||||
//! Default contruction, no private impl will be created e.i. the count is not valid
|
||||
shared_count()
|
||||
: m_impl(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//! Explicit construction of the shared_count requiring the raw JNI pointer to manage
|
||||
//! and a custom allocator to handle the private impl count [de]allocations
|
||||
//! \param javaObject Raw JNI pointer from the JVM
|
||||
//! \param allocator Custom allocator used only for the private impl count [de]allocations
|
||||
template<typename JniType, typename Allocator>
|
||||
shared_count(JniType javaObject, const Allocator& allocator)
|
||||
: m_impl(nullptr)
|
||||
{
|
||||
static_assert(AZStd::is_convertible<JniType, jobject>::value, "Specified type is not convertible to jobject.");
|
||||
|
||||
typedef sr_counted_impl<JniType, Allocator> impl_type;
|
||||
|
||||
Allocator a2(allocator);
|
||||
m_impl = reinterpret_cast<sp_counted_base*>(a2.allocate(sizeof(impl_type), AZStd::alignment_of<impl_type>::value));
|
||||
if (m_impl)
|
||||
{
|
||||
new(m_impl)impl_type(javaObject, allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to allocate the shared count for JNI::shared_ref. Releasing reference from JVM.");
|
||||
DeleteRef(javaObject);
|
||||
}
|
||||
}
|
||||
|
||||
//! Copy the shared count, increase the count if valid
|
||||
shared_count(shared_count const& rhs)
|
||||
: m_impl(rhs.m_impl)
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
m_impl->add_ref_copy();
|
||||
}
|
||||
}
|
||||
|
||||
//! Move the shared count, will invalidate the private impl of of the moved shared_count
|
||||
shared_count(shared_count&& rhs)
|
||||
: m_impl(rhs.m_impl)
|
||||
{
|
||||
rhs.m_impl = nullptr;
|
||||
}
|
||||
|
||||
//! Decrease the shared count, if valid, on deletion
|
||||
~shared_count()
|
||||
{
|
||||
if (m_impl)
|
||||
{
|
||||
m_impl->release();
|
||||
}
|
||||
}
|
||||
|
||||
//! Copy the shared count. Increases the new count, if valid; decreases the old count, if valid
|
||||
shared_count& operator =(shared_count const& rhs)
|
||||
{
|
||||
sp_counted_base* tmp = rhs.m_impl;
|
||||
|
||||
if (tmp != m_impl)
|
||||
{
|
||||
if (tmp)
|
||||
{
|
||||
tmp->add_ref_copy();
|
||||
}
|
||||
|
||||
if (m_impl)
|
||||
{
|
||||
m_impl->release();
|
||||
}
|
||||
|
||||
m_impl = tmp;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Check to see if two shared_counts are managing the same private impl pointer
|
||||
bool operator ==(shared_count const& rhs)
|
||||
{
|
||||
return (m_impl == rhs.m_impl);
|
||||
}
|
||||
|
||||
//! Swap the private impl pointers between two shared_counts
|
||||
void swap(shared_count& rhs)
|
||||
{
|
||||
AZStd::swap(m_impl, rhs.m_impl);
|
||||
}
|
||||
|
||||
//! Get the number of reference held by the shared count, if valid
|
||||
long use_count() const
|
||||
{
|
||||
return (m_impl != nullptr ? m_impl->use_count() : 0);
|
||||
}
|
||||
|
||||
//! Check to see if the shared_count is the only one holding on to the private
|
||||
//! impl pointer
|
||||
bool unique() const
|
||||
{
|
||||
return (use_count() == 1);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//! The private impl of the shared_count. This object is the one responsible for
|
||||
//! releasing the JNI reference with the JVM.
|
||||
sp_counted_base* m_impl;
|
||||
};
|
||||
}
|
||||
|
||||
//! A shared_ref works in the same way a AZStd::shared_ptr except it's specificially
|
||||
//! designed for the opaque pointer JNI types (e.g. jobject, jarray, etc.). Guarantees
|
||||
//! the java object is released from the JNI environment once the last shared_ref pointing
|
||||
//! to is released.
|
||||
template<typename JniType>
|
||||
class shared_ref
|
||||
{
|
||||
static_assert(AZStd::is_convertible<JniType, jobject>::value, "Specified type is not convertible to jobject.");
|
||||
|
||||
typedef shared_ref<JniType> ThisType;
|
||||
|
||||
public:
|
||||
typedef JniType ThisType::* UnspecifiedBoolType;
|
||||
|
||||
|
||||
// ---
|
||||
|
||||
//! Construct a default shared_ref with a null raw JNI pointer
|
||||
shared_ref()
|
||||
: m_javaObject(nullptr)
|
||||
, m_count()
|
||||
{
|
||||
}
|
||||
|
||||
//! Explicit construction of shared_ref with a null raw JNI pointer
|
||||
shared_ref(AZStd::nullptr_t)
|
||||
: shared_ref()
|
||||
{
|
||||
}
|
||||
|
||||
//! Only allow explicit construction from the raw pointer to the java object reference.
|
||||
//! Will use the AZ::SytemAllocator for the shared count allocations
|
||||
//! \param javaObject Raw pointer to the java object. Currently only supports Local and
|
||||
//! Global reference types. Weak Global reference are NOT supported.
|
||||
explicit shared_ref(JniType javaObject)
|
||||
: m_javaObject(javaObject)
|
||||
, m_count(javaObject, AZStd::allocator())
|
||||
{
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
if (m_javaObject)
|
||||
{
|
||||
int refType = GetRefType(m_javaObject);
|
||||
AZ_Error("JNI::shared_ref",
|
||||
refType == JNIGlobalRefType || refType == JNILocalRefType,
|
||||
"Unsupported JNI reference type (%d) used in JNI::shared_ref. "
|
||||
"Supported reference types are JNIGlobalRefType and JNILocalRefType. "
|
||||
"This may lead to unexpected behaviour.", refType);
|
||||
}
|
||||
#endif // defined(AZ_ENABLE_TRACING)
|
||||
}
|
||||
|
||||
//! Create a shared_ref with a custom allocator.
|
||||
//! NOTE: The custom allocator is only used for allocating the shared_count
|
||||
//! \param javaObject Raw pointer to the java object. Currently only supports Local and
|
||||
//! Global reference types. Weak Global reference are NOT supported.
|
||||
//! \param allocator Custom allocator for usage within the shared count.
|
||||
template<typename Allocator>
|
||||
shared_ref(JniType javaObject, const Allocator& allocator)
|
||||
: m_javaObject(javaObject)
|
||||
, m_count(javaObject, allocator)
|
||||
{
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
if (m_javaObject)
|
||||
{
|
||||
int refType = GetRefType(m_javaObject);
|
||||
AZ_Error("JNI::shared_ref",
|
||||
refType == JNIGlobalRefType || refType == JNILocalRefType,
|
||||
"Unsupported JNI reference type (%d) used in JNI::shared_ref. "
|
||||
"Supported reference types are JNIGlobalRefType and JNILocalRefType. "
|
||||
"This may lead to unexpected behaviour.", refType);
|
||||
}
|
||||
#endif // defined(AZ_ENABLE_TRACING)
|
||||
}
|
||||
|
||||
//! Make a copy of the shared_ref, increase the reference count
|
||||
//! \param rhs The shared_ref to copy
|
||||
explicit shared_ref(const shared_ref& rhs)
|
||||
: m_javaObject(rhs.m_javaObject)
|
||||
, m_count(rhs.m_count)
|
||||
{
|
||||
}
|
||||
|
||||
//! Polymorphic copy of a shared_ref
|
||||
//! \param rhs The shared_ref of a derived JNI pointer type to be copied
|
||||
template<typename Y>
|
||||
shared_ref(const shared_ref<Y>& rhs, typename AZStd::enable_if<AZStd::is_convertible<Y, JniType>::value, Y>::type = AZStd::nullptr_t())
|
||||
: m_javaObject(rhs.m_javaObject)
|
||||
, m_count(rhs.m_count)
|
||||
{
|
||||
}
|
||||
|
||||
//! Move the shared_ref from one shared_ref to another, Ctor
|
||||
//! \param rhs The shared_ref to be moved
|
||||
shared_ref(shared_ref&& rhs)
|
||||
: m_javaObject(rhs.m_javaObject)
|
||||
, m_count()
|
||||
{
|
||||
m_count.swap(rhs.m_count);
|
||||
rhs.m_javaObject = nullptr;
|
||||
}
|
||||
|
||||
//! Polymorphic move the shared_ref from one shared_ref to another, Ctor
|
||||
//! \param rhs The shared_ref to be moved
|
||||
template<typename Y>
|
||||
shared_ref(shared_ref<Y>&& rhs)
|
||||
: m_javaObject(rhs.m_javaObject)
|
||||
, m_count()
|
||||
{
|
||||
m_count.swap(rhs.m_count);
|
||||
rhs.m_javaObject = nullptr;
|
||||
}
|
||||
|
||||
//! Make a copy of the shared_ref, increase the reference count
|
||||
//! \param rhs The shared_ref to copy
|
||||
shared_ref& operator=(const shared_ref& rhs) // never throws
|
||||
{
|
||||
ThisType(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Make a polymorphic copy of the shared_ref, increase the reference count
|
||||
//! \param rhs The shared_ref to copy
|
||||
template<typename Y>
|
||||
shared_ref& operator=(const shared_ref<Y>& rhs)
|
||||
{
|
||||
ThisType(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Move the shared_ref from one shared_ref to another
|
||||
//! \param rhs The shared_ref to be moved
|
||||
shared_ref& operator=(shared_ref&& rhs)
|
||||
{
|
||||
ThisType(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Polymorphic move of a shared_ref from one shared_ref to another
|
||||
//! \param rhs The shared_ref to be moved
|
||||
template<typename Y>
|
||||
shared_ref& operator=(shared_ref<Y>&& rhs)
|
||||
{
|
||||
ThisType(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Determine if two shared_refs are the same
|
||||
//! \param rhs The shared_ref to compare against, may be of another JNI pointer type
|
||||
//! \return True if the raw pointers are the same, False otherwise
|
||||
template<typename Y>
|
||||
bool operator==(const shared_ref<Y>& rhs) const
|
||||
{
|
||||
return this->get() == rhs.get();
|
||||
}
|
||||
|
||||
//! Determine if two shared_refs are not the same
|
||||
//! \param rhs The shared_ref to compare against, may be of another JNI pointer type
|
||||
//! \return True if the raw pointers are the same, False otherwise
|
||||
template<typename Y>
|
||||
bool operator!=(const shared_ref<Y>& rhs) const
|
||||
{
|
||||
return this->get() != rhs.get();
|
||||
}
|
||||
|
||||
//! Compatibilty with the 'not' operator for validity checks. Only checkes for raw
|
||||
//! pointer validity, NOT if it's pointing to a null reference (weak global ref).
|
||||
bool operator !() const
|
||||
{
|
||||
return (m_javaObject == nullptr);
|
||||
}
|
||||
|
||||
//! Operator for implicit bool conversions
|
||||
//! \return 'True' if internal reference is valid, False otherwise
|
||||
operator UnspecifiedBoolType() const
|
||||
{
|
||||
return (m_javaObject == nullptr ? nullptr : &ThisType::m_javaObject);
|
||||
}
|
||||
|
||||
//! Explicit accessor of the raw pointer to the java reference.
|
||||
//! \return The raw pointer to the java object reference
|
||||
JniType get() const
|
||||
{
|
||||
return m_javaObject;
|
||||
}
|
||||
|
||||
//! Check to see if the shared_ref is the only one holding on to the raw JNI pointer
|
||||
//! \return True if the only refernece, False othewise
|
||||
bool unique() const
|
||||
{
|
||||
return m_count.unique();
|
||||
}
|
||||
|
||||
//! Get the number of reference held on the raw JNI pointer
|
||||
long use_count() const
|
||||
{
|
||||
return m_count.use_count();
|
||||
}
|
||||
|
||||
//! Swap the internal reference with another shared_ref of the same type
|
||||
//! \param lhs The shared_ref (of same type) to be swaped with
|
||||
void swap(shared_ref& lhs)
|
||||
{
|
||||
AZStd::swap(m_javaObject, lhs.m_javaObject);
|
||||
m_count.swap(lhs.m_count);
|
||||
}
|
||||
|
||||
//! Default reset of the internal reference to nullptr
|
||||
void reset()
|
||||
{
|
||||
ThisType().swap(*this);
|
||||
}
|
||||
|
||||
//! Reset the internal reference with a new pointer
|
||||
//! \param javaObject Raw pointer to the java object. Must be of same type.
|
||||
void reset(JniType javaObject)
|
||||
{
|
||||
// Pointer level self reset. Triggering this assert will cause a crash when either this
|
||||
// reference is used, or when the this scoped ref is cleaned up (double/invalid delete).
|
||||
AZ_Assert(javaObject == nullptr || javaObject != m_javaObject, "JNI::shared_ref pointer level self reset!");
|
||||
|
||||
// JNI reference level "self" reset. The references themselves are different so this is a
|
||||
// valid reset, however the underlining java object the references are pointing to
|
||||
// is the same in this case. As far as the JNI environment is concerned this is ok
|
||||
// but we should still make note of these occurrences.
|
||||
// NOTE: This warning will also trigger in the event the pointers are the same.
|
||||
AZ_Warning("JNI::shared_ref", GetEnv()->IsSameObject(m_javaObject, javaObject) == JNI_FALSE, "JNI::shared_ref JNI reference level self reset.");
|
||||
|
||||
ThisType(javaObject).swap(*this);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template<class Y> friend class shared_ref;
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
JniType m_javaObject; //!< Raw pointer of the java object reference (e.g. jobject, jarray, etc.)
|
||||
Internal::shared_count m_count; //!< Shared reference count, responsible for releaseing the JNI reference from the JVM
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,467 +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/Android/Utils.h>
|
||||
#include <AzCore/Android/JNI/JNI.h>
|
||||
#include <AzCore/Android/JNI/Object.h>
|
||||
#include <AzCore/Android/JNI/Signature.h>
|
||||
|
||||
// Include Testing Framework Here
|
||||
|
||||
|
||||
using namespace AZ::Android;
|
||||
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
struct SimpleJavaObject
|
||||
{
|
||||
SimpleJavaObject()
|
||||
: m_classRef(nullptr)
|
||||
, m_objectRef(nullptr)
|
||||
{
|
||||
m_classRef = JNI::LoadClass("com/amazon/test/SimpleObject");
|
||||
|
||||
JNIEnv* jniEnv = JNI::GetEnv();
|
||||
|
||||
jmethodID constructorMethodId = jniEnv->GetMethodID(m_classRef, "<init>", "()V");
|
||||
jobject localObjectRef = jniEnv->NewObject(m_classRef, constructorMethodId);
|
||||
|
||||
m_objectRef = jniEnv->NewGlobalRef(localObjectRef);
|
||||
|
||||
jniEnv->DeleteLocalRef(localObjectRef);
|
||||
}
|
||||
|
||||
~SimpleJavaObject()
|
||||
{
|
||||
JNI::DeleteRef(m_objectRef);
|
||||
}
|
||||
|
||||
jclass m_classRef;
|
||||
jobject m_objectRef;
|
||||
};
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
TEST(Signature, Sanity)
|
||||
{
|
||||
EXPECT_EQ(1, 1);
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
// Generation Tests
|
||||
// ----
|
||||
|
||||
TEST(Signature, Generate_NoArgs_IsEmptyString)
|
||||
{
|
||||
AZStd::string emptyStr = JNI::GetSignature();
|
||||
ASSERT_TRUE(emptyStr.empty());
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultNativeBooleanTypes_IsZ)
|
||||
{
|
||||
AZStd::string nativeTrueType = JNI::GetSignature(true);
|
||||
ASSERT_STREQ(nativeTrueType.c_str(), "Z");
|
||||
|
||||
AZStd::string nativeFalseType = JNI::GetSignature(false);
|
||||
ASSERT_STREQ(nativeFalseType.c_str(), "Z");
|
||||
|
||||
AZStd::string boolType = JNI::GetSignature(bool());
|
||||
ASSERT_STREQ(boolType.c_str(), "Z");
|
||||
|
||||
AZStd::string allBoolTypes = JNI::GetSignature(true, false, bool());
|
||||
ASSERT_STREQ(allBoolTypes.c_str(), "ZZZ");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJBooleanTypes_IsZ)
|
||||
{
|
||||
AZStd::string jniTrueType = JNI::GetSignature(JNI_TRUE);
|
||||
ASSERT_STREQ(jniTrueType.c_str(), "Z");
|
||||
|
||||
AZStd::string jniFalseType = JNI::GetSignature(JNI_FALSE);
|
||||
ASSERT_STREQ(jniFalseType.c_str(), "Z");
|
||||
|
||||
AZStd::string jboolType = JNI::GetSignature(jboolean());
|
||||
ASSERT_STREQ(jboolType.c_str(), "Z");
|
||||
|
||||
AZStd::string jniBoolArrayType = JNI::GetSignature(jbooleanArray());
|
||||
ASSERT_STREQ(jniBoolArrayType.c_str(), "[Z");
|
||||
|
||||
AZStd::string allJBoolTypes = JNI::GetSignature(JNI_TRUE, JNI_FALSE, jboolean(), jbooleanArray());
|
||||
ASSERT_STREQ(allJBoolTypes.c_str(), "ZZZ[Z");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_AllDefaultBooleanTypes_IsZ)
|
||||
{
|
||||
AZStd::string allBoolTypes = JNI::GetSignature(true, false, bool(), JNI_TRUE, JNI_FALSE, jboolean(), jbooleanArray());
|
||||
ASSERT_STREQ(allBoolTypes.c_str(), "ZZZZZZ[Z");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJByteTypes_IsB)
|
||||
{
|
||||
AZStd::string jbyteType = JNI::GetSignature(jbyte());
|
||||
ASSERT_STREQ(jbyteType.c_str(), "B");
|
||||
|
||||
AZStd::string jbyteArrayType = JNI::GetSignature(jbyteArray());
|
||||
ASSERT_STREQ(jbyteArrayType.c_str(), "[B");
|
||||
|
||||
AZStd::string allJByteTypes = JNI::GetSignature(jbyte(), jbyteArray());
|
||||
ASSERT_STREQ(allJByteTypes.c_str(), "B[B");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJCharTypes_IsC)
|
||||
{
|
||||
AZStd::string jcharType = JNI::GetSignature(jchar());
|
||||
ASSERT_STREQ(jcharType.c_str(), "C");
|
||||
|
||||
AZStd::string jcharArrayType = JNI::GetSignature(jcharArray());
|
||||
ASSERT_STREQ(jcharArrayType.c_str(), "[C");
|
||||
|
||||
AZStd::string allJCharTypes = JNI::GetSignature(jchar(), jcharArray());
|
||||
ASSERT_STREQ(allJCharTypes.c_str(), "C[C");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJShortTypes_IsS)
|
||||
{
|
||||
AZStd::string jshortType = JNI::GetSignature(jshort());
|
||||
ASSERT_STREQ(jshortType.c_str(), "S");
|
||||
|
||||
AZStd::string jshortArrayType = JNI::GetSignature(jshortArray());
|
||||
ASSERT_STREQ(jshortArrayType.c_str(), "[S");
|
||||
|
||||
AZStd::string allJShortTypes = JNI::GetSignature(jshort(), jshortArray());
|
||||
ASSERT_STREQ(allJShortTypes.c_str(), "S[S");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJIntTypes_IsI)
|
||||
{
|
||||
AZStd::string jintType = JNI::GetSignature(jint());
|
||||
ASSERT_STREQ(jintType.c_str(), "I");
|
||||
|
||||
AZStd::string jintArrayType = JNI::GetSignature(jintArray());
|
||||
ASSERT_STREQ(jintArrayType.c_str(), "[I");
|
||||
|
||||
AZStd::string allJIntTypes = JNI::GetSignature(jint(), jintArray());
|
||||
ASSERT_STREQ(allJIntTypes.c_str(), "I[I");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJLongTypes_IsJ)
|
||||
{
|
||||
AZStd::string jlongType = JNI::GetSignature(jlong());
|
||||
ASSERT_STREQ(jlongType.c_str(), "J");
|
||||
|
||||
AZStd::string jlongArrayType = JNI::GetSignature(jlongArray());
|
||||
ASSERT_STREQ(jlongArrayType.c_str(), "[J");
|
||||
|
||||
AZStd::string allJLongTypes = JNI::GetSignature(jlong(), jlongArray());
|
||||
ASSERT_STREQ(allJLongTypes.c_str(), "J[J");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJFloatTypes_IsF)
|
||||
{
|
||||
AZStd::string jfloatType = JNI::GetSignature(jfloat());
|
||||
ASSERT_STREQ(jfloatType.c_str(), "F");
|
||||
|
||||
AZStd::string jfloatArrayType = JNI::GetSignature(jfloatArray());
|
||||
ASSERT_STREQ(jfloatArrayType.c_str(), "[F");
|
||||
|
||||
AZStd::string allJFloatTypes = JNI::GetSignature(jfloat(), jfloatArray());
|
||||
ASSERT_STREQ(allJFloatTypes.c_str(), "F[F");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJDoubleTypes_IsD)
|
||||
{
|
||||
AZStd::string jdoubleType = JNI::GetSignature(jdouble());
|
||||
ASSERT_STREQ(jdoubleType.c_str(), "D");
|
||||
|
||||
AZStd::string jdoubleArrayType = JNI::GetSignature(jdoubleArray());
|
||||
ASSERT_STREQ(jdoubleArrayType.c_str(), "[D");
|
||||
|
||||
AZStd::string allJDoubleTypes = JNI::GetSignature(jdouble(), jdoubleArray());
|
||||
ASSERT_STREQ(allJDoubleTypes.c_str(), "D[D");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJStringTypes_IsLjava_lang_String)
|
||||
{
|
||||
AZStd::string jstringType = JNI::GetSignature(jstring());
|
||||
ASSERT_STREQ(jstringType.c_str(), "Ljava/lang/String;");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJClassTypes_IsLjava_lang_Class)
|
||||
{
|
||||
AZStd::string jclassType = JNI::GetSignature(jclass());
|
||||
ASSERT_STREQ(jclassType.c_str(), "Ljava/lang/Class;");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJObjectType_IsEmptyString)
|
||||
{
|
||||
AZStd::string jobjectType = JNI::GetSignature(jobject());
|
||||
ASSERT_TRUE(jobjectType.empty());
|
||||
|
||||
AZStd::string jobjectArrayType = JNI::GetSignature(jobjectArray());
|
||||
ASSERT_TRUE(jobjectArrayType.empty());
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_SimpleJObjectType_IsLcom_amazon_test_SimpleObject)
|
||||
{
|
||||
SimpleJavaObject simpleObject;
|
||||
|
||||
AZStd::string simpleObjectType = JNI::GetSignature(simpleObject.m_objectRef);
|
||||
ASSERT_STREQ(simpleObjectType.c_str(), "Lcom/amazon/test/SimpleObject;");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_AllDefaultPrimtiveTypes_IsZZZBBCCSSIIJJFFDD)
|
||||
{
|
||||
AZStd::string allPrimitiveTypes = JNI::GetSignature(
|
||||
bool(), jboolean(), jbooleanArray(),
|
||||
jbyte(), jbyteArray(),
|
||||
jchar(), jcharArray(),
|
||||
jshort(), jshortArray(),
|
||||
jint(), jintArray(),
|
||||
jlong(), jlongArray(),
|
||||
jfloat(), jfloatArray(),
|
||||
jdouble(), jdoubleArray()
|
||||
);
|
||||
ASSERT_STREQ(allPrimitiveTypes.c_str(), "ZZ[ZB[BC[CS[SI[IJ[JF[FD[D");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_DefaultJStringJClassTypes_IsLjava_lang_StringLjava_lang_Class)
|
||||
{
|
||||
AZStd::string jstringJClassTypes = JNI::GetSignature(jstring(), jclass());
|
||||
ASSERT_STREQ(jstringJClassTypes.c_str(), "Ljava/lang/String;Ljava/lang/Class;");
|
||||
}
|
||||
|
||||
TEST(Signature, Generate_AllTypes_IsZZZBBCCSSIIJJFFDDLjava_lang_StringLjava_lang_ClassLcom_amazon_test_SimpleObject)
|
||||
{
|
||||
SimpleJavaObject simpleObject;
|
||||
|
||||
AZStd::string allTypes = JNI::GetSignature(
|
||||
bool(), jboolean(), jbooleanArray(),
|
||||
jbyte(), jbyteArray(),
|
||||
jchar(), jcharArray(),
|
||||
jshort(), jshortArray(),
|
||||
jint(), jintArray(),
|
||||
jlong(), jlongArray(),
|
||||
jfloat(), jfloatArray(),
|
||||
jdouble(), jdoubleArray(),
|
||||
jstring(), jclass(),
|
||||
simpleObject.m_objectRef
|
||||
);
|
||||
ASSERT_STREQ(allTypes.c_str(), "ZZ[ZB[BC[CS[SI[IJ[JF[FD[DLjava/lang/String;Ljava/lang/Class;Lcom/amazon/test/SimpleObject;");
|
||||
}
|
||||
|
||||
|
||||
// ----
|
||||
// Validation Tests
|
||||
// ----
|
||||
|
||||
|
||||
TEST(Signature, Validate_NoArgs_IsEmptyString)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature(""));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultNativeBooleanTypes_IsZ)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Z", true));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Z", false));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Z", bool()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJBooleanTypes_IsZ)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Z", JNI_TRUE));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Z", JNI_FALSE));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Z", jboolean()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[Z", jbooleanArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultBooleanTypes_IsZ)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("ZZZ", true, false, bool()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("ZZZ[Z", JNI_TRUE, JNI_FALSE, jboolean(), jbooleanArray()));
|
||||
|
||||
ASSERT_TRUE(JNI::ValidateSignature("ZZZZZZ[Z",
|
||||
true, false, bool(),
|
||||
JNI_TRUE, JNI_FALSE, jboolean(),
|
||||
jbooleanArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJByteTypes_IsB)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("B", jbyte()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[B", jbyteArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJByteTypes_IsB)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("B[B", jbyte(), jbyteArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJCharTypes_IsC)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("C", jchar()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[C", jcharArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJCharTypes_IsC)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("C[C", jchar(), jcharArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJShortTypes_IsS)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("S", jshort()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[S", jshortArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJShortTypes_IsS)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("S[S", jshort(), jshortArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJIntTypes_IsI)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("I", jint()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[I", jintArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJIntTypes_IsI)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("I[I", jint(), jintArray()));
|
||||
}
|
||||
TEST(Signature, Validate_DefaultJLongTypes_IsJ)
|
||||
{
|
||||
|
||||
ASSERT_TRUE(JNI::ValidateSignature("J", jlong()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[J", jlongArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJLongTypes_IsJ)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("J[J", jlong(), jlongArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJFloatTypes_IsF)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("F", jfloat()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[F", jfloatArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJFloatTypes_IsF)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("F[F", jfloat(), jfloatArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_DefaultJDoubleTypes_IsD)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("D", jdouble()));
|
||||
ASSERT_TRUE(JNI::ValidateSignature("[D", jdoubleArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultJDoubleTypes_IsD)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature("D[D", jdouble(), jdoubleArray()));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllDefaultPrimtiveTypes_IsZZZBBCCSSIIJJFFDD)
|
||||
{
|
||||
ASSERT_TRUE(JNI::ValidateSignature(
|
||||
"ZZ[ZB[BC[CS[SI[IJ[JF[FD[D",
|
||||
bool(), jboolean(), jbooleanArray(),
|
||||
jbyte(), jbyteArray(),
|
||||
jchar(), jcharArray(),
|
||||
jshort(), jshortArray(),
|
||||
jint(), jintArray(),
|
||||
jlong(), jlongArray(),
|
||||
jfloat(), jfloatArray(),
|
||||
jdouble(), jdoubleArray()
|
||||
));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_JClass_IsL_java_lang_Class)
|
||||
{
|
||||
jclass signatureClass = JNI::LoadClass("com/amazon/test/SimpleObject");
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Ljava/lang/Class;", signatureClass));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_JString_IsL_java_lang_String)
|
||||
{
|
||||
JNIEnv* jniEnv = JNI::GetEnv();
|
||||
jstring javaString = jniEnv->NewStringUTF("Test");
|
||||
EXPECT_TRUE(JNI::ValidateSignature("Ljava/lang/String;", javaString));
|
||||
jniEnv->DeleteLocalRef(javaString);
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_SimpleJObjectType_IsLcom_amazon_test_SimpleObject)
|
||||
{
|
||||
SimpleJavaObject simpleObject;
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Lcom/amazon/test/SimpleObject;", simpleObject.m_objectRef));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_PolymorphicActivityType_IsLandroid_app_Activity)
|
||||
{
|
||||
jobject activity = Utils::GetActivityRef();
|
||||
ASSERT_TRUE(JNI::ValidateSignature("Landroid/app/Activity;", activity));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_JStringJClass_IsLjava_lang_StringLjava_lang_Class)
|
||||
{
|
||||
JNIEnv* jniEnv = JNI::GetEnv();
|
||||
jclass signatureClass = JNI::LoadClass("com/amazon/test/SimpleObject");
|
||||
jstring javaString = jniEnv->NewStringUTF("Test");
|
||||
|
||||
EXPECT_TRUE(JNI::ValidateSignature("Ljava/lang/String;Ljava/lang/Class;", javaString, signatureClass));
|
||||
jniEnv->DeleteLocalRef(javaString);
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_AllTypes_IsZZZBBCCSSIIJJFFDDL_java_lang_StringL_java_lang_ClassLcom_amazon_test_SimpleObjectLandroid_app_Activity)
|
||||
{
|
||||
JNIEnv* jniEnv = JNI::GetEnv();
|
||||
jstring javaString = jniEnv->NewStringUTF("Test");
|
||||
|
||||
SimpleJavaObject simpleObject;
|
||||
|
||||
jobject activity = Utils::GetActivityRef();
|
||||
|
||||
ASSERT_TRUE(JNI::ValidateSignature(
|
||||
"ZZ[ZB[BC[CS[SI[IJ[JF[FD[DLjava/lang/String;Ljava/lang/Class;Lcom/amazon/test/SimpleObject;Landroid/app/Activity;",
|
||||
bool(), jboolean(), jbooleanArray(),
|
||||
jbyte(), jbyteArray(),
|
||||
jchar(), jcharArray(),
|
||||
jshort(), jshortArray(),
|
||||
jint(), jintArray(),
|
||||
jlong(), jlongArray(),
|
||||
jfloat(), jfloatArray(),
|
||||
jdouble(), jdoubleArray(),
|
||||
javaString,
|
||||
simpleObject.m_classRef,
|
||||
simpleObject.m_objectRef,
|
||||
activity
|
||||
));
|
||||
|
||||
jniEnv->DeleteLocalRef(javaString);
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_ExtraParams_IsFalse)
|
||||
{
|
||||
ASSERT_FALSE(JNI::ValidateSignature("Z", JNI_TRUE, JNI_TRUE));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_MissingParams_IsFalse)
|
||||
{
|
||||
ASSERT_FALSE(JNI::ValidateSignature("ZZ", JNI_TRUE));
|
||||
}
|
||||
|
||||
TEST(Signature, Validate_WrongParms_IsFalse)
|
||||
{
|
||||
ASSERT_FALSE(JNI::ValidateSignature("ZI", JNI_TRUE, jfloat()));
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
*
|
||||
*/
|
||||
#include <AzCore/Android/Utils.h>
|
||||
#include <AzCore/Android/AndroidEnv.h>
|
||||
#include <AzCore/Android/APKFileHandler.h>
|
||||
#include <AzCore/Android/JNI/Object.h>
|
||||
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace Utils
|
||||
{
|
||||
namespace
|
||||
{
|
||||
////////////////////////////////////////////////////////////////
|
||||
constexpr const char* GetApkAssetsPrefix()
|
||||
{
|
||||
return "/APK";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
jclass GetActivityClassRef()
|
||||
{
|
||||
return AndroidEnv::Get()->GetActivityClassRef();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
jobject GetActivityRef()
|
||||
{
|
||||
return AndroidEnv::Get()->GetActivityRef();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AAssetManager* GetAssetManager()
|
||||
{
|
||||
return AndroidEnv::Get()->GetAssetManager();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AConfiguration* GetConfiguration()
|
||||
{
|
||||
return AndroidEnv::Get()->GetConfiguration();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void UpdateConfiguration()
|
||||
{
|
||||
return AndroidEnv::Get()->UpdateConfiguration();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* GetAppPrivateStoragePath()
|
||||
{
|
||||
return AndroidEnv::Get()->GetAppPrivateStoragePath();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* GetAppPublicStoragePath()
|
||||
{
|
||||
return AndroidEnv::Get()->GetAppPublicStoragePath();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* GetObbStoragePath()
|
||||
{
|
||||
return AndroidEnv::Get()->GetObbStoragePath();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* GetPackageName()
|
||||
{
|
||||
return AndroidEnv::Get()->GetPackageName();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
int GetAppVersionCode()
|
||||
{
|
||||
return AndroidEnv::Get()->GetAppVersionCode();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* GetObbFileName(bool mainFile)
|
||||
{
|
||||
return AndroidEnv::Get()->GetObbFileName(mainFile);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
bool IsApkPath(const char* filePath)
|
||||
{
|
||||
return AZ::IO::PathView(filePath).IsRelativeTo(AZ::IO::PathView(GetApkAssetsPrefix()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
AZ::IO::FixedMaxPath StripApkPrefix(const char* filePath)
|
||||
{
|
||||
constexpr AZ::IO::PathView apkPrefixView = GetApkAssetsPrefix();
|
||||
return AZ::IO::PathView(filePath).LexicallyProximate(apkPrefixView);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
const char* FindAssetsDirectory()
|
||||
{
|
||||
#if defined(LY_NO_ASSETS)
|
||||
// The TestRunner app which runs unit tests does not have any assets.
|
||||
return GetAppPublicStoragePath();
|
||||
#endif
|
||||
#if !defined(_RELEASE)
|
||||
// first check to see if they are in public storage (application specific)
|
||||
const char* publicAppStorage = GetAppPublicStoragePath();
|
||||
|
||||
OSString path = OSString::format("%s/engine.json", publicAppStorage);
|
||||
AZ_TracePrintf("Android::Utils", "Searching for %s\n", path.c_str());
|
||||
|
||||
FILE* f = fopen(path.c_str(), "r");
|
||||
if (f != nullptr)
|
||||
{
|
||||
fclose(f);
|
||||
return publicAppStorage;
|
||||
}
|
||||
#endif // !defined(_RELEASE)
|
||||
|
||||
// if they aren't in public storage, they are in private storage (APK)
|
||||
AAssetManager* mgr = GetAssetManager();
|
||||
if (mgr)
|
||||
{
|
||||
AAsset* asset = AAssetManager_open(mgr, "engine.json", AASSET_MODE_UNKNOWN);
|
||||
if (asset)
|
||||
{
|
||||
AAsset_close(asset);
|
||||
return GetApkAssetsPrefix();
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Assert(false, "Failed to locate the engine.json path");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void ShowSplashScreen()
|
||||
{
|
||||
JNI::Internal::Object<AZ::OSAllocator> activity(GetActivityClassRef(), GetActivityRef());
|
||||
|
||||
activity.RegisterMethod("ShowSplashScreen", "()V");
|
||||
activity.InvokeVoidMethod("ShowSplashScreen");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void DismissSplashScreen()
|
||||
{
|
||||
JNI::Internal::Object<AZ::OSAllocator> activity(GetActivityClassRef(), GetActivityRef());
|
||||
|
||||
activity.RegisterMethod("DismissSplashScreen", "()V");
|
||||
activity.InvokeVoidMethod("DismissSplashScreen");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
ANativeWindow* GetWindow()
|
||||
{
|
||||
return AndroidEnv::Get()->GetWindow();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
bool GetWindowSize(int& widthPixels, int& heightPixels)
|
||||
{
|
||||
ANativeWindow* window = GetWindow();
|
||||
if (window)
|
||||
{
|
||||
widthPixels = ANativeWindow_getWidth(window);
|
||||
heightPixels = ANativeWindow_getHeight(window);
|
||||
|
||||
// should an error occur from the above functions a negative value will be returned
|
||||
return (widthPixels > 0 && heightPixels > 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
void SetLoadFilesToMemory(const char* fileNames)
|
||||
{
|
||||
APKFileHandler::SetLoadFilesToMemory(fileNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +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/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/configuration.h>
|
||||
#include <android/native_window.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Android
|
||||
{
|
||||
namespace Utils
|
||||
{
|
||||
//! Request the global reference to the activity class
|
||||
jclass GetActivityClassRef();
|
||||
|
||||
//! Request the global reference to the activity instance
|
||||
jobject GetActivityRef();
|
||||
|
||||
//! Get the global pointer to the Android asset manager, which is used for APK file i/o.
|
||||
AAssetManager* GetAssetManager();
|
||||
|
||||
//! Get the global pointer to the device/application configuration,
|
||||
AConfiguration* GetConfiguration();
|
||||
|
||||
//! If the AndroidEnv owns the native configuration, it will be updated with the latest configuration
|
||||
//! information, otherwise nothing will happen.
|
||||
void UpdateConfiguration();
|
||||
|
||||
//! Get the hidden internal storage, typically this is where the application is installed
|
||||
//! on the device.
|
||||
//! e.g. /data/data/<package_name>/files
|
||||
const char* GetAppPrivateStoragePath();
|
||||
|
||||
//! Get the application specific directory for public public storage.
|
||||
//! e.g. <public_storage>/Android/data/<package_name>/files
|
||||
const char* GetAppPublicStoragePath();
|
||||
|
||||
//! Get the application specific directory for obb files.
|
||||
//! e.g. <public_storage>/Android/obb/<package_name>/files
|
||||
const char* GetObbStoragePath();
|
||||
|
||||
//! Get the dot separated package name for the current application.
|
||||
//! e.g. com.o3de.samples for SamplesProject
|
||||
const char* GetPackageName();
|
||||
|
||||
//! Get the app version code (android:versionCode in the manifest).
|
||||
int GetAppVersionCode();
|
||||
|
||||
//! Get the filename of the obb. This doesn't include the path to the obb folder.
|
||||
const char* GetObbFileName(bool mainFile);
|
||||
|
||||
//! Check to see if the path is prefixed with "/APK"
|
||||
bool IsApkPath(const char* filePath);
|
||||
|
||||
//! Will first check to verify the argument is an apk asset path and if so
|
||||
//! will strip the prefix from the path.
|
||||
//! \return The pointer position of the relative asset path
|
||||
AZ::IO::FixedMaxPath StripApkPrefix(const char* filePath);
|
||||
|
||||
//! Searches application storage and the APK for engine.json. Will return nullptr
|
||||
//! if engine.json is not found.
|
||||
const char* FindAssetsDirectory();
|
||||
|
||||
//! Calls into Java to show the splash screen on the main UI (Java) thread
|
||||
void ShowSplashScreen();
|
||||
|
||||
//! Calls into Java to dismiss the splash screen on the main UI (Java) thread
|
||||
void DismissSplashScreen();
|
||||
|
||||
//! Get the native android window
|
||||
ANativeWindow* GetWindow();
|
||||
|
||||
//! Query the pixel dimensions of the window
|
||||
//! \param[out] widthPixels Returns the pixel width of the window
|
||||
//! \param[out] heightPixels Returns the pixel height of the window
|
||||
//! \return True if successful, False otherwise
|
||||
bool GetWindowSize(int& widthPixels, int& heightPixels);
|
||||
|
||||
//! Set the filenames for files to be loaded to memory
|
||||
void SetLoadFilesToMemory(const char* fileNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(...)
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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>;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user