Merge branch 'development' into optimization/unused_files
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> # Conflicts: # Code/Editor/IEditorImpl.cpp # Code/Editor/IEditorImpl.h # Gems/LmbrCentral/Code/Tests/lmbrcentral_editor_tests_files.cmake
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzCore/std/typetraits/is_base_of.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/IO/IStreamerTypes.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
@@ -7,11 +7,62 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetDataStream.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/IO/IStreamer.h>
|
||||
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/std/parallel/condition_variable.h>
|
||||
|
||||
namespace AZ::Data
|
||||
{
|
||||
namespace DataStreamInternal
|
||||
{
|
||||
struct AssetDataStreamPrivate
|
||||
{
|
||||
//! Optional data buffer that's been directly passed in through Open(), instead of reading data from a file.
|
||||
AZStd::vector<AZ::u8> m_preloadedData;
|
||||
//! The current active streamer read request - tracked in case we need to cancel it prematurely
|
||||
AZ::IO::FileRequestPtr m_curReadRequest{ nullptr };
|
||||
|
||||
//! Synchronization for the read request, so that it's possible to block until completion.
|
||||
AZStd::mutex m_readRequestMutex;
|
||||
AZStd::condition_variable m_readRequestActive;
|
||||
|
||||
void SetReadRequest(AZ::IO::FileRequestPtr&& req)
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::mutex> lock(m_readRequestMutex);
|
||||
// The read request finished, so stop tracking it.
|
||||
m_curReadRequest = AZStd::move(req);
|
||||
}
|
||||
void BlockUntilReadComplete()
|
||||
{
|
||||
AZStd::unique_lock lock(m_readRequestMutex);
|
||||
m_readRequestActive.wait(
|
||||
lock,
|
||||
[this]
|
||||
{
|
||||
return m_curReadRequest == nullptr;
|
||||
});
|
||||
lock.unlock();
|
||||
}
|
||||
void CancelRequest()
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::mutex> lock(m_readRequestMutex);
|
||||
if (m_curReadRequest)
|
||||
{
|
||||
auto streamer = Interface<IO::IStreamer>::Get();
|
||||
m_curReadRequest = streamer->Cancel(m_curReadRequest);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace Internal
|
||||
|
||||
AssetDataStream::AssetDataStream(AZ::IO::IStreamerTypes::RequestMemoryAllocator* bufferAllocator)
|
||||
: m_bufferAllocator(bufferAllocator ? bufferAllocator : &m_defaultAllocator)
|
||||
: m_privateData(AZStd::make_unique<DataStreamInternal::AssetDataStreamPrivate>())
|
||||
, m_bufferAllocator(bufferAllocator ? bufferAllocator : &m_defaultAllocator)
|
||||
{
|
||||
ClearInternalStateData();
|
||||
}
|
||||
@@ -53,9 +104,9 @@ namespace AZ::Data
|
||||
OpenInternal(data.size(), "(mem buffer)");
|
||||
|
||||
// Directly take ownership of the provided buffer
|
||||
m_preloadedData = AZStd::move(data);
|
||||
m_buffer = m_preloadedData.data();
|
||||
m_loadedSize = m_preloadedData.size();
|
||||
m_privateData->m_preloadedData = AZStd::move(data);
|
||||
m_buffer = m_privateData->m_preloadedData.data();
|
||||
m_loadedSize = m_privateData->m_preloadedData.size();
|
||||
}
|
||||
|
||||
void AssetDataStream::Open(const AZStd::string& filePath, size_t fileOffset, size_t assetSize,
|
||||
@@ -65,7 +116,7 @@ namespace AZ::Data
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
AZ_Assert(!m_isOpen, "Attempting to open the stream when it is already open.");
|
||||
AZ_Assert(!m_curReadRequest, "Queueing an asset stream load while one is still in progress.");
|
||||
AZ_Assert(!m_privateData->m_curReadRequest, "Queueing an asset stream load while one is still in progress.");
|
||||
AZ_Assert(!filePath.empty(), "AssetDataStream::Open called without a valid file name.");
|
||||
|
||||
// Initialize the state variables and start tracking the overall load timings
|
||||
@@ -97,11 +148,8 @@ namespace AZ::Data
|
||||
"Buffer for %s was expected to be %zu bytes, but is %zu bytes.",
|
||||
m_filePath.c_str(), m_requestedAssetSize, m_loadedSize);
|
||||
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::mutex> lock(m_readRequestMutex);
|
||||
// The read request finished, so stop tracking it.
|
||||
m_curReadRequest = nullptr;
|
||||
}
|
||||
// The read request finished, so stop tracking it.
|
||||
m_privateData->SetReadRequest(nullptr);
|
||||
|
||||
// Call the load callback to start processing the loaded data.
|
||||
if (loadCallback)
|
||||
@@ -115,21 +163,22 @@ namespace AZ::Data
|
||||
}
|
||||
|
||||
// Notify that the load is complete, in case anyone is using BlockUntilLoadComplete to block.
|
||||
m_readRequestActive.notify_one();
|
||||
m_privateData->m_readRequestActive.notify_one();
|
||||
};
|
||||
|
||||
// Queue the raw file load with the file streamer.
|
||||
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
|
||||
m_curReadRequest = streamer->Read(
|
||||
m_privateData->m_curReadRequest =
|
||||
streamer->Read(
|
||||
m_filePath,
|
||||
*m_bufferAllocator,
|
||||
m_requestedAssetSize,
|
||||
deadline, priority, m_fileOffset);
|
||||
m_curDeadline = deadline;
|
||||
m_curPriority = priority;
|
||||
streamer->SetRequestCompleteCallback(m_curReadRequest, streamerCallback);
|
||||
streamer->SetRequestCompleteCallback(m_privateData->m_curReadRequest, streamerCallback);
|
||||
|
||||
streamer->QueueRequest(m_curReadRequest);
|
||||
streamer->QueueRequest(m_privateData->m_curReadRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,19 +188,19 @@ namespace AZ::Data
|
||||
loadCallback(AZ::IO::IStreamerTypes::RequestStatus::Completed);
|
||||
}
|
||||
|
||||
m_readRequestActive.notify_one();
|
||||
m_privateData->m_readRequestActive.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetDataStream::Reschedule(AZStd::chrono::milliseconds newDeadline, AZ::IO::IStreamerTypes::Priority newPriority)
|
||||
{
|
||||
if (m_curReadRequest && (newDeadline < m_curDeadline || newPriority > m_curPriority))
|
||||
if (m_privateData->m_curReadRequest && (newDeadline < m_curDeadline || newPriority > m_curPriority))
|
||||
{
|
||||
auto deadline = AZStd::GetMin(m_curDeadline, newDeadline);
|
||||
auto priority = AZStd::GetMax(m_curPriority, newPriority);
|
||||
|
||||
auto streamer = Interface<IO::IStreamer>::Get();
|
||||
m_curReadRequest = streamer->RescheduleRequest(m_curReadRequest, deadline, priority);
|
||||
m_privateData->m_curReadRequest = streamer->RescheduleRequest(m_privateData->m_curReadRequest, deadline, priority);
|
||||
m_curDeadline = deadline;
|
||||
m_curPriority = priority;
|
||||
}
|
||||
@@ -159,15 +208,13 @@ namespace AZ::Data
|
||||
|
||||
void AssetDataStream::BlockUntilLoadComplete()
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_readRequestMutex);
|
||||
m_readRequestActive.wait(lock, [this] { return m_curReadRequest == nullptr; });
|
||||
lock.unlock();
|
||||
m_privateData->BlockUntilReadComplete();
|
||||
}
|
||||
|
||||
void AssetDataStream::ClearInternalStateData()
|
||||
{
|
||||
// Clear all our internal state data.
|
||||
m_preloadedData.resize(0);
|
||||
m_privateData->m_preloadedData.resize(0);
|
||||
m_buffer = nullptr;
|
||||
m_loadedSize = 0;
|
||||
m_requestedAssetSize = 0;
|
||||
@@ -204,10 +251,10 @@ namespace AZ::Data
|
||||
void AssetDataStream::Close()
|
||||
{
|
||||
AZ_Assert(m_isOpen, "Attempting to close a stream that hasn't been opened.");
|
||||
AZ_Assert(m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight.");
|
||||
AZ_Assert(m_privateData->m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight.");
|
||||
|
||||
// Destroy the asset buffer and unlock the allocator, so the allocator itself knows that it is no longer needed.
|
||||
if (m_buffer != m_preloadedData.data())
|
||||
if (m_buffer != m_privateData->m_preloadedData.data())
|
||||
{
|
||||
m_bufferAllocator->Release(m_buffer);
|
||||
}
|
||||
@@ -221,12 +268,7 @@ namespace AZ::Data
|
||||
|
||||
void AssetDataStream::RequestCancel()
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::mutex> lock(m_readRequestMutex);
|
||||
if (m_curReadRequest)
|
||||
{
|
||||
auto streamer = Interface<IO::IStreamer>::Get();
|
||||
m_curReadRequest = streamer->Cancel(m_curReadRequest);
|
||||
}
|
||||
m_privateData->CancelRequest();
|
||||
}
|
||||
|
||||
void AssetDataStream::Seek(AZ::IO::OffsetType bytes, AZ::IO::GenericStream::SeekMode mode)
|
||||
|
||||
@@ -9,17 +9,26 @@
|
||||
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/IStreamerTypes.h>
|
||||
#include <AzCore/IO/IStreamer.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/parallel/condition_variable.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template<class T, class Allocator>
|
||||
class vector;
|
||||
}
|
||||
|
||||
namespace AZ::Data
|
||||
{
|
||||
namespace DataStreamInternal
|
||||
{
|
||||
struct AssetDataStreamPrivate;
|
||||
}
|
||||
|
||||
class AssetDataStream : public AZ::IO::GenericStream
|
||||
{
|
||||
public:
|
||||
using VectorDataSource = AZStd::vector<AZ::u8, AZStd::allocator>;
|
||||
// The default Generic Stream APIs in this class will only allow for a single sequential pass
|
||||
// through the data, no seeking. Reads will block when pages aren't available yet, and
|
||||
// pages will be marked for recycling once reading has progressed beyond them.
|
||||
@@ -29,10 +38,10 @@ namespace AZ::Data
|
||||
~AssetDataStream() override;
|
||||
|
||||
// Open the AssetDataStream and make a copy of the provided memory buffer.
|
||||
void Open(const AZStd::vector<AZ::u8>& data);
|
||||
void Open(const VectorDataSource& data);
|
||||
|
||||
// Open the AssetDataStream and directly take ownership of a pre-populated memory buffer.
|
||||
void Open(AZStd::vector<AZ::u8>&& data);
|
||||
void Open(VectorDataSource&& data);
|
||||
|
||||
// Open the AssetDataStream and load it via file streaming
|
||||
using OnCompleteCallback = AZStd::function<void(AZ::IO::IStreamerTypes::RequestStatus)>;
|
||||
@@ -91,6 +100,8 @@ namespace AZ::Data
|
||||
|
||||
void ClearInternalStateData();
|
||||
|
||||
AZStd::unique_ptr<DataStreamInternal::AssetDataStreamPrivate> m_privateData;
|
||||
|
||||
//! The allocator to use for allocating / deallocating asset buffers
|
||||
AZ::IO::IStreamerTypes::RequestMemoryAllocator* m_bufferAllocator{ nullptr };
|
||||
|
||||
@@ -106,9 +117,6 @@ namespace AZ::Data
|
||||
//! The amount of data that's expected to be loaded.
|
||||
size_t m_requestedAssetSize{ 0 };
|
||||
|
||||
//! Optional data buffer that's been directly passed in through Open(), instead of reading data from a file.
|
||||
AZStd::vector<AZ::u8> m_preloadedData;
|
||||
|
||||
//! The buffer that will hold the raw data after it's loaded from the file.
|
||||
void* m_buffer{ nullptr };
|
||||
|
||||
@@ -119,19 +127,12 @@ namespace AZ::Data
|
||||
//! The current offset representing how far we've read into the buffer.
|
||||
size_t m_curOffset{ 0 };
|
||||
|
||||
//! The current active streamer read request - tracked in case we need to cancel it prematurely
|
||||
AZ::IO::FileRequestPtr m_curReadRequest{ nullptr };
|
||||
|
||||
//! The current request deadline. Used to avoid requesting a reschedule to the same (current) deadline.
|
||||
AZStd::chrono::milliseconds m_curDeadline{ AZ::IO::IStreamerTypes::s_noDeadline };
|
||||
|
||||
//! The current request priority. Used to avoid requesting a reschedule to the same (current) priority.
|
||||
AZ::IO::IStreamerTypes::Priority m_curPriority{ AZ::IO::IStreamerTypes::s_priorityMedium };
|
||||
|
||||
//! Synchronization for the read request, so that it's possible to block until completion.
|
||||
AZStd::mutex m_readRequestMutex;
|
||||
AZStd::condition_variable m_readRequestActive;
|
||||
|
||||
//! Track whether or not the stream is currently open
|
||||
bool m_isOpen{ false };
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzCore/Asset/AssetManager_private.h>
|
||||
#include <AzCore/Asset/AssetDataStream.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/IO/IStreamer.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <AzCore/Asset/AssetContainer.h>
|
||||
#include <AzCore/Asset/AssetDataStream.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h> // used as allocator for most components
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* 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/Casting/numeric_cast.h>
|
||||
#include <AzCore/DOM/DomPath.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
#include <AzCore/Console/ConsoleTypeHelpers.h>
|
||||
|
||||
namespace AZ::Dom
|
||||
{
|
||||
PathEntry::PathEntry(size_t value)
|
||||
: m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
PathEntry::PathEntry(AZ::Name value)
|
||||
: m_value(AZStd::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
PathEntry::PathEntry(AZStd::string_view value)
|
||||
: m_value(AZ::Name(value))
|
||||
{
|
||||
}
|
||||
|
||||
PathEntry& PathEntry::operator=(size_t value)
|
||||
{
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathEntry& PathEntry::operator=(AZ::Name value)
|
||||
{
|
||||
m_value = AZStd::move(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathEntry& PathEntry::operator=(AZStd::string_view value)
|
||||
{
|
||||
m_value = AZ::Name(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool PathEntry::operator==(const PathEntry& other) const
|
||||
{
|
||||
return m_value == other.m_value;
|
||||
}
|
||||
|
||||
bool PathEntry::operator==(size_t value) const
|
||||
{
|
||||
return IsIndex() && GetIndex() == value;
|
||||
}
|
||||
|
||||
bool PathEntry::operator==(const AZ::Name& key) const
|
||||
{
|
||||
return IsKey() && GetKey() == key;
|
||||
}
|
||||
|
||||
bool PathEntry::operator==(AZStd::string_view key) const
|
||||
{
|
||||
return IsKey() && GetKey() == AZ::Name(key);
|
||||
}
|
||||
|
||||
bool PathEntry::operator!=(const PathEntry& other) const
|
||||
{
|
||||
return m_value != other.m_value;
|
||||
}
|
||||
|
||||
bool PathEntry::operator!=(size_t value) const
|
||||
{
|
||||
return !IsIndex() || GetIndex() != value;
|
||||
}
|
||||
|
||||
bool PathEntry::operator!=(const AZ::Name& key) const
|
||||
{
|
||||
return !IsKey() || GetKey() != key;
|
||||
}
|
||||
|
||||
bool PathEntry::operator!=(AZStd::string_view key) const
|
||||
{
|
||||
return !IsKey() || GetKey() != AZ::Name(key);
|
||||
}
|
||||
|
||||
void PathEntry::SetEndOfArray()
|
||||
{
|
||||
m_value = EndOfArrayIndex;
|
||||
}
|
||||
|
||||
bool PathEntry::IsEndOfArray() const
|
||||
{
|
||||
const size_t* result = AZStd::get_if<size_t>(&m_value);
|
||||
return result == nullptr ? false : ((*result) == EndOfArrayIndex);
|
||||
}
|
||||
|
||||
bool PathEntry::IsIndex() const
|
||||
{
|
||||
const size_t* result = AZStd::get_if<size_t>(&m_value);
|
||||
return result == nullptr ? false : ((*result) != EndOfArrayIndex);
|
||||
}
|
||||
|
||||
bool PathEntry::IsKey() const
|
||||
{
|
||||
return AZStd::holds_alternative<AZ::Name>(m_value);
|
||||
}
|
||||
|
||||
size_t PathEntry::GetIndex() const
|
||||
{
|
||||
AZ_Assert(IsIndex(), "GetIndex called on PathEntry that is not an index");
|
||||
return AZStd::get<size_t>(m_value);
|
||||
}
|
||||
|
||||
const AZ::Name& PathEntry::GetKey() const
|
||||
{
|
||||
AZ_Assert(IsKey(), "Key called on PathEntry that is not a key");
|
||||
return AZStd::get<AZ::Name>(m_value);
|
||||
}
|
||||
|
||||
Path::Path(AZStd::initializer_list<PathEntry> init)
|
||||
: m_entries(init)
|
||||
{
|
||||
}
|
||||
|
||||
Path::Path(AZStd::string_view pathString)
|
||||
{
|
||||
FromString(pathString);
|
||||
}
|
||||
|
||||
Path Path::operator/(const PathEntry& entry) const
|
||||
{
|
||||
Path newPath(*this);
|
||||
newPath /= entry;
|
||||
return newPath;
|
||||
}
|
||||
|
||||
Path Path::operator/(size_t index) const
|
||||
{
|
||||
return *this / PathEntry(index);
|
||||
}
|
||||
|
||||
Path Path::operator/(AZ::Name key) const
|
||||
{
|
||||
return *this / PathEntry(key);
|
||||
}
|
||||
|
||||
Path Path::operator/(AZStd::string_view key) const
|
||||
{
|
||||
return *this / PathEntry(key);
|
||||
}
|
||||
|
||||
Path Path::operator/(const Path& other) const
|
||||
{
|
||||
Path newPath(*this);
|
||||
newPath /= other;
|
||||
return newPath;
|
||||
}
|
||||
|
||||
Path& Path::operator/=(const PathEntry& entry)
|
||||
{
|
||||
Push(entry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Path& Path::operator/=(size_t index)
|
||||
{
|
||||
return *this /= PathEntry(index);
|
||||
}
|
||||
|
||||
Path& Path::operator/=(AZ::Name key)
|
||||
{
|
||||
return *this /= PathEntry(key);
|
||||
}
|
||||
|
||||
Path& Path::operator/=(AZStd::string_view key)
|
||||
{
|
||||
return *this /= PathEntry(key);
|
||||
}
|
||||
|
||||
Path& Path::operator/=(const Path& other)
|
||||
{
|
||||
for (const PathEntry& entry : other)
|
||||
{
|
||||
Push(entry);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Path::operator==(const Path& other) const
|
||||
{
|
||||
return m_entries == other.m_entries;
|
||||
}
|
||||
|
||||
const Path::ContainerType& Path::GetEntries() const
|
||||
{
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
void Path::Push(PathEntry entry)
|
||||
{
|
||||
m_entries.push_back(AZStd::move(entry));
|
||||
}
|
||||
|
||||
void Path::Push(size_t entry)
|
||||
{
|
||||
Push(PathEntry(entry));
|
||||
}
|
||||
|
||||
void Path::Push(AZ::Name entry)
|
||||
{
|
||||
Push(PathEntry(AZStd::move(entry)));
|
||||
}
|
||||
|
||||
void Path::Push(AZStd::string_view entry)
|
||||
{
|
||||
Push(AZ::Name(entry));
|
||||
}
|
||||
|
||||
void Path::Pop()
|
||||
{
|
||||
m_entries.pop_back();
|
||||
}
|
||||
|
||||
void Path::Clear()
|
||||
{
|
||||
m_entries.clear();
|
||||
}
|
||||
|
||||
PathEntry Path::At(size_t index) const
|
||||
{
|
||||
if (index < m_entries.size())
|
||||
{
|
||||
return m_entries[index];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t Path::Size() const
|
||||
{
|
||||
return m_entries.size();
|
||||
}
|
||||
|
||||
PathEntry& Path::operator[](size_t index)
|
||||
{
|
||||
return m_entries[index];
|
||||
}
|
||||
|
||||
const PathEntry& Path::operator[](size_t index) const
|
||||
{
|
||||
return m_entries[index];
|
||||
}
|
||||
|
||||
Path::ContainerType::iterator Path::begin()
|
||||
{
|
||||
return m_entries.begin();
|
||||
}
|
||||
|
||||
Path::ContainerType::iterator Path::end()
|
||||
{
|
||||
return m_entries.end();
|
||||
}
|
||||
|
||||
Path::ContainerType::const_iterator Path::begin() const
|
||||
{
|
||||
return m_entries.cbegin();
|
||||
}
|
||||
|
||||
Path::ContainerType::const_iterator Path::end() const
|
||||
{
|
||||
return m_entries.cend();
|
||||
}
|
||||
|
||||
Path::ContainerType::const_iterator Path::cbegin() const
|
||||
{
|
||||
return m_entries.cbegin();
|
||||
}
|
||||
|
||||
Path::ContainerType::const_iterator Path::cend() const
|
||||
{
|
||||
return m_entries.cend();
|
||||
}
|
||||
|
||||
size_t Path::size() const
|
||||
{
|
||||
return m_entries.size();
|
||||
}
|
||||
|
||||
size_t Path::GetStringLength() const
|
||||
{
|
||||
size_t size = 0;
|
||||
for (const PathEntry& entry : m_entries)
|
||||
{
|
||||
++size;
|
||||
if (entry.IsEndOfArray())
|
||||
{
|
||||
size += 1;
|
||||
}
|
||||
else if (entry.IsIndex())
|
||||
{
|
||||
const size_t index = entry.GetIndex();
|
||||
const double digitCount = index > 0 ? log10(aznumeric_cast<double>(index + 1)) : 1.0;
|
||||
size += aznumeric_cast<size_t>(ceil(digitCount));
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* nameBuffer = entry.GetKey().GetCStr();
|
||||
for (size_t i = 0; nameBuffer[i]; ++i)
|
||||
{
|
||||
if (nameBuffer[i] == EscapeCharacter || nameBuffer[i] == PathSeparator)
|
||||
{
|
||||
++size;
|
||||
}
|
||||
++size;
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void Path::FormatString(char* stringBuffer, size_t bufferSize) const
|
||||
{
|
||||
size_t bufferIndex = 0;
|
||||
|
||||
auto putChar = [&](char c)
|
||||
{
|
||||
if (bufferIndex == bufferSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stringBuffer[bufferIndex++] = c;
|
||||
};
|
||||
|
||||
auto writeToBuffer = [&](const char* key)
|
||||
{
|
||||
for (size_t keyIndex = 0; key[keyIndex]; ++keyIndex)
|
||||
{
|
||||
const char c = key[keyIndex];
|
||||
if (c == EscapeCharacter)
|
||||
{
|
||||
putChar(EscapeCharacter);
|
||||
putChar(TildeSequence);
|
||||
}
|
||||
else if (c == PathSeparator)
|
||||
{
|
||||
putChar(EscapeCharacter);
|
||||
putChar(ForwardSlashSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
putChar(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const PathEntry& entry : m_entries)
|
||||
{
|
||||
putChar(PathSeparator);
|
||||
if (entry.IsEndOfArray())
|
||||
{
|
||||
putChar(EndOfArrayCharacter);
|
||||
}
|
||||
else if (entry.IsIndex())
|
||||
{
|
||||
bufferIndex += azsnprintf(&stringBuffer[bufferIndex], bufferSize - bufferIndex, "%zu", entry.GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
writeToBuffer(entry.GetKey().GetCStr());
|
||||
}
|
||||
}
|
||||
|
||||
putChar('\0');
|
||||
}
|
||||
|
||||
AZStd::string Path::ToString() const
|
||||
{
|
||||
AZStd::string formattedString;
|
||||
const size_t size = GetStringLength();
|
||||
formattedString.resize_no_construct(size);
|
||||
FormatString(formattedString.data(), size + 1);
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
void Path::AppendToString(AZStd::string& output) const
|
||||
{
|
||||
const size_t startIndex = output.length();
|
||||
const size_t stringLength = GetStringLength();
|
||||
output.resize_no_construct(startIndex + stringLength);
|
||||
FormatString(output.data() + startIndex, stringLength + 1);
|
||||
}
|
||||
|
||||
void Path::FromString(AZStd::string_view pathString)
|
||||
{
|
||||
m_entries.clear();
|
||||
if (pathString.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pathEntryCount = 0;
|
||||
for (size_t i = 1; i <= pathString.size(); ++i)
|
||||
{
|
||||
if (pathString[i] == PathSeparator)
|
||||
{
|
||||
++pathEntryCount;
|
||||
}
|
||||
}
|
||||
m_entries.reserve(pathEntryCount);
|
||||
|
||||
// Ignore a preceeding path separator and start processing after it
|
||||
size_t pathIndex = pathString[0] == PathSeparator ? 1 : 0;
|
||||
bool isNumber = true;
|
||||
AZStd::string convertedSection;
|
||||
for (size_t i = pathIndex; i <= pathString.size(); ++i)
|
||||
{
|
||||
if (i == pathString.size() || pathString[i] == PathSeparator)
|
||||
{
|
||||
AZStd::string_view section = pathString.substr(pathIndex, i - pathIndex);
|
||||
if (section.size() == 1 && section[0] == EndOfArrayCharacter)
|
||||
{
|
||||
PathEntry entry;
|
||||
entry.SetEndOfArray();
|
||||
m_entries.push_back(AZStd::move(entry));
|
||||
}
|
||||
else if (isNumber && !section.empty())
|
||||
{
|
||||
size_t index = 0;
|
||||
ConsoleTypeHelpers::StringToValue(index, section);
|
||||
m_entries.push_back(PathEntry{ index });
|
||||
}
|
||||
else
|
||||
{
|
||||
convertedSection.clear();
|
||||
size_t lastPos = 0;
|
||||
size_t posToEscape = section.find(EscapeCharacter);
|
||||
while (posToEscape != AZStd::string_view::npos)
|
||||
{
|
||||
if (convertedSection.empty())
|
||||
{
|
||||
convertedSection.reserve(section.size() - 1);
|
||||
}
|
||||
convertedSection += section.substr(lastPos, posToEscape - lastPos);
|
||||
if (section[posToEscape + 1] == ForwardSlashSequence)
|
||||
{
|
||||
convertedSection += '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
convertedSection += '~';
|
||||
}
|
||||
|
||||
lastPos = posToEscape + 2;
|
||||
posToEscape = section.find(EscapeCharacter, posToEscape + 2);
|
||||
}
|
||||
|
||||
if (!convertedSection.empty())
|
||||
{
|
||||
convertedSection += section.substr(lastPos);
|
||||
m_entries.emplace_back(convertedSection);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entries.emplace_back(section);
|
||||
}
|
||||
}
|
||||
pathIndex = i + 1;
|
||||
isNumber = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char c = pathString[i];
|
||||
isNumber = isNumber && c >= '0' && c <= '9';
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Dom
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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/Name/Name.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
namespace AZ::Dom
|
||||
{
|
||||
//! Represents the path to a direct descendant of a Value.
|
||||
//! PathEntry may be one of the following:
|
||||
//! - Index, a numerical index for indexing within Arrays and Nodes
|
||||
//! - Key, a name for indexing within Objects and Nodes
|
||||
//! - EndOfArray, a special-case indicator for representing the end of an array
|
||||
//! used by the patching system to represent push / pop back operations.
|
||||
class PathEntry final
|
||||
{
|
||||
public:
|
||||
static constexpr size_t EndOfArrayIndex = size_t(-1);
|
||||
|
||||
PathEntry() = default;
|
||||
PathEntry(const PathEntry&) = default;
|
||||
PathEntry(PathEntry&&) = default;
|
||||
explicit PathEntry(size_t value);
|
||||
explicit PathEntry(AZ::Name value);
|
||||
explicit PathEntry(AZStd::string_view value);
|
||||
|
||||
PathEntry& operator=(const PathEntry&) = default;
|
||||
PathEntry& operator=(PathEntry&&) = default;
|
||||
PathEntry& operator=(size_t value);
|
||||
PathEntry& operator=(AZ::Name value);
|
||||
PathEntry& operator=(AZStd::string_view value);
|
||||
|
||||
bool operator==(const PathEntry& other) const;
|
||||
bool operator==(size_t index) const;
|
||||
bool operator==(const AZ::Name& key) const;
|
||||
bool operator==(AZStd::string_view key) const;
|
||||
bool operator!=(const PathEntry& other) const;
|
||||
bool operator!=(size_t index) const;
|
||||
bool operator!=(const AZ::Name& key) const;
|
||||
bool operator!=(AZStd::string_view key) const;
|
||||
|
||||
void SetEndOfArray();
|
||||
|
||||
bool IsEndOfArray() const;
|
||||
bool IsIndex() const;
|
||||
bool IsKey() const;
|
||||
|
||||
size_t GetIndex() const;
|
||||
const AZ::Name& GetKey() const;
|
||||
|
||||
private:
|
||||
AZStd::variant<size_t, AZ::Name> m_value;
|
||||
};
|
||||
|
||||
//! Represents a path, represented as a series of PathEntry values, to a position in a Value.
|
||||
class Path final
|
||||
{
|
||||
public:
|
||||
using ContainerType = AZStd::vector<PathEntry>;
|
||||
static constexpr char PathSeparator = '/';
|
||||
static constexpr char EscapeCharacter = '~';
|
||||
static constexpr char TildeSequence = '0';
|
||||
static constexpr char ForwardSlashSequence = '1';
|
||||
static constexpr char EndOfArrayCharacter = '-';
|
||||
|
||||
Path() = default;
|
||||
Path(const Path&) = default;
|
||||
Path(Path&&) = default;
|
||||
explicit Path(AZStd::initializer_list<PathEntry> init);
|
||||
//! Creates a Path from a path string, a path string is formatted per the JSON pointer specification
|
||||
//! and looks like "/path/to/value/0"
|
||||
explicit Path(AZStd::string_view pathString);
|
||||
|
||||
template<class InputIterator>
|
||||
explicit Path(InputIterator first, InputIterator last)
|
||||
: m_entries(first, last)
|
||||
{
|
||||
}
|
||||
|
||||
Path& operator=(const Path&) = default;
|
||||
Path& operator=(Path&&) = default;
|
||||
|
||||
Path operator/(const PathEntry&) const;
|
||||
Path operator/(size_t) const;
|
||||
Path operator/(AZ::Name) const;
|
||||
Path operator/(AZStd::string_view) const;
|
||||
Path operator/(const Path&) const;
|
||||
|
||||
Path& operator/=(const PathEntry&);
|
||||
Path& operator/=(size_t);
|
||||
Path& operator/=(AZ::Name);
|
||||
Path& operator/=(AZStd::string_view);
|
||||
Path& operator/=(const Path&);
|
||||
|
||||
bool operator==(const Path&) const;
|
||||
|
||||
const ContainerType& GetEntries() const;
|
||||
void Push(PathEntry entry);
|
||||
void Push(size_t entry);
|
||||
void Push(AZ::Name entry);
|
||||
void Push(AZStd::string_view key);
|
||||
void Pop();
|
||||
void Clear();
|
||||
PathEntry At(size_t index) const;
|
||||
size_t Size() const;
|
||||
|
||||
PathEntry& operator[](size_t index);
|
||||
const PathEntry& operator[](size_t index) const;
|
||||
|
||||
ContainerType::iterator begin();
|
||||
ContainerType::iterator end();
|
||||
ContainerType::const_iterator begin() const;
|
||||
ContainerType::const_iterator end() const;
|
||||
ContainerType::const_iterator cbegin() const;
|
||||
ContainerType::const_iterator cend() const;
|
||||
size_t size() const;
|
||||
|
||||
//! Gets the length this path would require, if string-formatted.
|
||||
//! The length includes the contents of the string but not a null terminator.
|
||||
size_t GetStringLength() const;
|
||||
//! Formats a JSON-pointer style path string into the target buffer.
|
||||
//! This operation will fail if bufferSize < GetStringLength() + 1
|
||||
void FormatString(char* stringBuffer, size_t bufferSize) const;
|
||||
//! Returns a JSON-pointer style path string for this path.
|
||||
AZStd::string ToString() const;
|
||||
void AppendToString(AZStd::string& output) const;
|
||||
//! Reads a JSON-pointer style path from pathString and replaces this path's contents.
|
||||
//! Paths are accepted in the following forms:
|
||||
//! "/path/to/foo/0"
|
||||
//! "path/to/foo/0"
|
||||
void FromString(AZStd::string_view pathString);
|
||||
|
||||
private:
|
||||
ContainerType m_entries;
|
||||
};
|
||||
} // namespace AZ::Dom
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomPath.h>
|
||||
#include <AzCore/DOM/DomValue.h>
|
||||
#include <AzCore/DOM/DomValueWriter.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
@@ -1177,4 +1178,124 @@ namespace AZ::Dom
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
Value& Value::operator[](const PathEntry& entry)
|
||||
{
|
||||
if (entry.IsEndOfArray())
|
||||
{
|
||||
Array::ContainerType& array = GetArrayInternal();
|
||||
array.push_back();
|
||||
return array[array.size() - 1];
|
||||
}
|
||||
return entry.IsIndex() ? operator[](entry.GetIndex()) : operator[](entry.GetKey());
|
||||
}
|
||||
|
||||
const Value& Value::operator[](const PathEntry& entry) const
|
||||
{
|
||||
return entry.IsIndex() ? operator[](entry.GetIndex()) : operator[](entry.GetKey());
|
||||
}
|
||||
|
||||
Value& Value::operator[](const Path& path)
|
||||
{
|
||||
Value* value = this;
|
||||
for (const PathEntry& entry : path)
|
||||
{
|
||||
value = &value->operator[](entry);
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
const Value& Value::operator[](const Path& path) const
|
||||
{
|
||||
const Value* value = this;
|
||||
for (const PathEntry& entry : path)
|
||||
{
|
||||
value = &value->operator[](entry);
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
const Value* Value::FindChild(const PathEntry& entry) const
|
||||
{
|
||||
if (entry.IsEndOfArray())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else if (entry.IsIndex())
|
||||
{
|
||||
const Array::ContainerType& array = GetArrayInternal();
|
||||
const size_t index = entry.GetIndex();
|
||||
if (index < array.size())
|
||||
{
|
||||
return &array[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Object::ContainerType& obj = GetObjectInternal();
|
||||
auto memberIt = FindMember(entry.GetKey());
|
||||
if (memberIt != obj.end())
|
||||
{
|
||||
return &memberIt->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value* Value::FindMutableChild(const PathEntry& entry)
|
||||
{
|
||||
if (entry.IsEndOfArray())
|
||||
{
|
||||
Array::ContainerType& array = GetArrayInternal();
|
||||
array.push_back();
|
||||
return &array[array.size() - 1];
|
||||
}
|
||||
else if (entry.IsIndex())
|
||||
{
|
||||
Array::ContainerType& array = GetArrayInternal();
|
||||
const size_t index = entry.GetIndex();
|
||||
if (index < array.size())
|
||||
{
|
||||
return &array[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Object::ContainerType& obj = GetObjectInternal();
|
||||
auto memberIt = FindMutableMember(entry.GetKey());
|
||||
if (memberIt != obj.end())
|
||||
{
|
||||
return &memberIt->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Value* Value::FindChild(const Path& path) const
|
||||
{
|
||||
const Value* value = this;
|
||||
for (const PathEntry& entry : path)
|
||||
{
|
||||
value = value->FindChild(entry);
|
||||
if (value == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Value* Value::FindMutableChild(const Path& path)
|
||||
{
|
||||
Value* value = this;
|
||||
for (const PathEntry& entry : path)
|
||||
{
|
||||
value = value->FindMutableChild(entry);
|
||||
if (value == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
} // namespace AZ::Dom
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
namespace AZ::Dom
|
||||
{
|
||||
class PathEntry;
|
||||
class Path;
|
||||
using KeyType = AZ::Name;
|
||||
|
||||
//! The type of underlying value stored in a value. \see Value
|
||||
@@ -380,6 +382,17 @@ namespace AZ::Dom
|
||||
Visitor::Result Accept(Visitor& visitor, bool copyStrings) const;
|
||||
AZStd::unique_ptr<Visitor> GetWriteHandler();
|
||||
|
||||
// Path API...
|
||||
Value& operator[](const PathEntry& entry);
|
||||
const Value& operator[](const PathEntry& entry) const;
|
||||
Value& operator[](const Path& path);
|
||||
const Value& operator[](const Path& path) const;
|
||||
|
||||
const Value* FindChild(const PathEntry& entry) const;
|
||||
Value* FindMutableChild(const PathEntry& entry);
|
||||
const Value* FindChild(const Path& path) const;
|
||||
Value* FindMutableChild(const Path& path);
|
||||
|
||||
//! Gets the internal value of this Value. Note that this value's types may not correspond one-to-one with the Type enumeration,
|
||||
//! as internally the same type might have different storage mechanisms. Where possible, prefer using the typed API.
|
||||
const ValueType& GetInternalValue() const;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
#include <AzCore/std/any.h>
|
||||
|
||||
// These Streamer includes need to be moved to Streamer internals/implementation,
|
||||
// and pull out only what we need for visibility at IStreamer.h interface declaration.
|
||||
#include <AzCore/IO/Streamer/Statistics.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
class ExternalFileRequest;
|
||||
class FileRequestHandle;
|
||||
|
||||
using FileRequestPtr = AZStd::intrusive_ptr<ExternalFileRequest>;
|
||||
/**
|
||||
* Data Streamer Interface
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -137,18 +137,18 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
ReadFile(request, args);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
@@ -166,7 +166,7 @@ namespace AZ::IO
|
||||
{
|
||||
Section& delayed = m_delayedSections.front();
|
||||
AZ_Assert(delayed.m_parent, "Delayed section doesn't have a reference to the original request.");
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&delayed.m_parent->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&delayed.m_parent->GetCommand());
|
||||
AZ_Assert(data, "A request in the delayed queue of the BlockCache didn't have a parent with read data.");
|
||||
// This call can add the same section to the back of the queue if there's not
|
||||
// enough space. Because of this the entry needs to be removed from the delayed
|
||||
@@ -233,7 +233,7 @@ namespace AZ::IO
|
||||
}
|
||||
}
|
||||
|
||||
void BlockCache::ReadFile(FileRequest* request, FileRequest::ReadData& data)
|
||||
void BlockCache::ReadFile(FileRequest* request, Requests::ReadData& data)
|
||||
{
|
||||
if (!m_next)
|
||||
{
|
||||
@@ -250,7 +250,7 @@ namespace AZ::IO
|
||||
m_numMetaDataRetrievalInProgress--;
|
||||
if (fileSizeRequest.GetStatus() == IStreamerTypes::RequestStatus::Completed)
|
||||
{
|
||||
auto& requestInfo = AZStd::get<FileRequest::FileMetaDataRetrievalData>(fileSizeRequest.GetCommand());
|
||||
auto& requestInfo = AZStd::get<Requests::FileMetaDataRetrievalData>(fileSizeRequest.GetCommand());
|
||||
if (requestInfo.m_found)
|
||||
{
|
||||
ContinueReadFile(request, requestInfo.m_fileSize);
|
||||
@@ -272,7 +272,7 @@ namespace AZ::IO
|
||||
Section main;
|
||||
Section epilog;
|
||||
|
||||
auto& data = AZStd::get<FileRequest::ReadData>(request->GetCommand());
|
||||
auto& data = AZStd::get<Requests::ReadData>(request->GetCommand());
|
||||
|
||||
if (!SplitRequest(prolog, main, epilog, data.m_path, fileLength, data.m_offset, data.m_size,
|
||||
reinterpret_cast<u8*>(data.m_output)))
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
class RequestPath;
|
||||
namespace Requests
|
||||
{
|
||||
struct ReadData;
|
||||
}
|
||||
|
||||
struct BlockCacheConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
@@ -109,7 +115,7 @@ namespace AZ::IO
|
||||
|
||||
using TimePoint = AZStd::chrono::system_clock::time_point;
|
||||
|
||||
void ReadFile(FileRequest* request, FileRequest::ReadData& data);
|
||||
void ReadFile(FileRequest* request, Requests::ReadData& data);
|
||||
void ContinueReadFile(FileRequest* request, u64 fileLength);
|
||||
CacheResult ReadFromCache(FileRequest* request, Section& section, const RequestPath& filePath);
|
||||
CacheResult ReadFromCache(FileRequest* request, Section& section, u32 cacheBlock);
|
||||
|
||||
@@ -101,12 +101,12 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::CreateDedicatedCacheData>)
|
||||
{
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::DestroyDedicatedCacheData>)
|
||||
{
|
||||
args.m_range = FileRange::CreateRangeForEntireFile();
|
||||
m_context->PushPreparedRequest(request);
|
||||
@@ -125,28 +125,28 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
ReadFile(request, args);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CreateDedicatedCacheData>)
|
||||
{
|
||||
CreateDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::DestroyDedicatedCacheData>)
|
||||
{
|
||||
DestroyDedicatedCache(request, args);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
@@ -200,7 +200,7 @@ namespace AZ::IO
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedCache::ReadFile(FileRequest* request, FileRequest::ReadData& data)
|
||||
void DedicatedCache::ReadFile(FileRequest* request, Requests::ReadData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_offset);
|
||||
if (index == s_fileNotFound)
|
||||
@@ -255,7 +255,7 @@ namespace AZ::IO
|
||||
StreamStackEntry::CollectStatistics(statistics);
|
||||
}
|
||||
|
||||
void DedicatedCache::CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data)
|
||||
void DedicatedCache::CreateDedicatedCache(FileRequest* request, Requests::CreateDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index == s_fileNotFound)
|
||||
@@ -276,7 +276,7 @@ namespace AZ::IO
|
||||
m_context->MarkRequestAsCompleted(request);
|
||||
}
|
||||
|
||||
void DedicatedCache::DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data)
|
||||
void DedicatedCache::DestroyDedicatedCache(FileRequest* request, Requests::DestroyDedicatedCacheData& data)
|
||||
{
|
||||
size_t index = FindCache(data.m_path, data.m_range);
|
||||
if (index != s_fileNotFound)
|
||||
|
||||
@@ -11,15 +11,21 @@
|
||||
#include <AzCore/IO/Streamer/BlockCache.h>
|
||||
#include <AzCore/IO/Streamer/FileRange.h>
|
||||
#include <AzCore/IO/Streamer/Statistics.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
#include <AzCore/IO/Streamer/StreamStackEntry.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace Requests
|
||||
{
|
||||
struct CreateDedicatedCacheData;
|
||||
struct DestroyDedicatedCacheData;
|
||||
} // namespace Requests
|
||||
|
||||
struct DedicatedCacheConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
@@ -56,16 +62,19 @@ namespace AZ::IO
|
||||
|
||||
void UpdateStatus(Status& status) const override;
|
||||
|
||||
void UpdateCompletionEstimates(AZStd::chrono::system_clock::time_point now, AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin, StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
void UpdateCompletionEstimates(
|
||||
AZStd::chrono::system_clock::time_point now,
|
||||
AZStd::vector<FileRequest*>& internalPending,
|
||||
StreamerContext::PreparedQueue::iterator pendingBegin,
|
||||
StreamerContext::PreparedQueue::iterator pendingEnd) override;
|
||||
|
||||
void CollectStatistics(AZStd::vector<Statistic>& statistics) const override;
|
||||
|
||||
private:
|
||||
void CreateDedicatedCache(FileRequest* request, FileRequest::CreateDedicatedCacheData& data);
|
||||
void DestroyDedicatedCache(FileRequest* request, FileRequest::DestroyDedicatedCacheData& data);
|
||||
void CreateDedicatedCache(FileRequest* request, Requests::CreateDedicatedCacheData& data);
|
||||
void DestroyDedicatedCache(FileRequest* request, Requests::DestroyDedicatedCacheData& data);
|
||||
|
||||
void ReadFile(FileRequest* request, FileRequest::ReadData& data);
|
||||
void ReadFile(FileRequest* request, Requests::ReadData& data);
|
||||
size_t FindCache(const RequestPath& filename, FileRange range);
|
||||
size_t FindCache(const RequestPath& filename, u64 offset);
|
||||
|
||||
|
||||
@@ -12,22 +12,30 @@
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/IO/Streamer/StreamerContext.h>
|
||||
|
||||
namespace AZ::IO
|
||||
//
|
||||
// Command structures.
|
||||
//
|
||||
|
||||
namespace AZ::IO::Requests
|
||||
{
|
||||
//
|
||||
// Command structures.
|
||||
//
|
||||
ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead)
|
||||
: m_path(path)
|
||||
, m_output(output)
|
||||
, m_outputSize(outputSize)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_sharedRead(sharedRead)
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::ExternalRequestData::ExternalRequestData(FileRequestPtr&& request)
|
||||
: m_request(AZStd::move(request))
|
||||
{}
|
||||
|
||||
FileRequest::RequestPathStoreData::RequestPathStoreData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{}
|
||||
|
||||
FileRequest::ReadRequestData::ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
ReadRequestData::ReadRequestData(
|
||||
RequestPath path,
|
||||
void* output,
|
||||
u64 outputSize,
|
||||
u64 offset,
|
||||
u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline,
|
||||
IStreamerTypes::Priority priority)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_allocator(nullptr)
|
||||
, m_deadline(deadline)
|
||||
@@ -37,10 +45,16 @@ namespace AZ::IO
|
||||
, m_size(size)
|
||||
, m_priority(priority)
|
||||
, m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally.
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::ReadRequestData::ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator,
|
||||
u64 offset, u64 size, AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority)
|
||||
ReadRequestData::ReadRequestData(
|
||||
RequestPath path,
|
||||
IStreamerTypes::RequestMemoryAllocator* allocator,
|
||||
u64 offset,
|
||||
u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline,
|
||||
IStreamerTypes::Priority priority)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_allocator(allocator)
|
||||
, m_deadline(deadline)
|
||||
@@ -50,9 +64,10 @@ namespace AZ::IO
|
||||
, m_size(size)
|
||||
, m_priority(priority)
|
||||
, m_memoryType(IStreamerTypes::MemoryType::ReadWrite) // Only generic memory can be assigned externally.
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::ReadRequestData::~ReadRequestData()
|
||||
ReadRequestData::~ReadRequestData()
|
||||
{
|
||||
if (m_allocator != nullptr)
|
||||
{
|
||||
@@ -64,65 +79,80 @@ namespace AZ::IO
|
||||
}
|
||||
}
|
||||
|
||||
FileRequest::ReadData::ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead)
|
||||
: m_output(output)
|
||||
, m_outputSize(outputSize)
|
||||
, m_path(path)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_sharedRead(sharedRead)
|
||||
{}
|
||||
CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize)
|
||||
DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalRequestData::ExternalRequestData(FileRequestPtr&& request)
|
||||
: m_request(AZStd::move(request))
|
||||
{
|
||||
}
|
||||
|
||||
RequestPathStoreData::RequestPathStoreData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{
|
||||
}
|
||||
|
||||
CompressedReadData::CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize)
|
||||
: m_compressionInfo(AZStd::move(compressionInfo))
|
||||
, m_output(output)
|
||||
, m_readOffset(readOffset)
|
||||
, m_readSize(readSize)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::FileExistsCheckData::FileExistsCheckData(const RequestPath& path)
|
||||
FileExistsCheckData::FileExistsCheckData(const RequestPath& path)
|
||||
: m_path(path)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path)
|
||||
FileMetaDataRetrievalData::FileMetaDataRetrievalData(const RequestPath& path)
|
||||
: m_path(path)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::CancelData::CancelData(FileRequestPtr target)
|
||||
CancelData::CancelData(FileRequestPtr target)
|
||||
: m_target(AZStd::move(target))
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::FlushData::FlushData(RequestPath path)
|
||||
FlushData::FlushData(RequestPath path)
|
||||
: m_path(AZStd::move(path))
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::RescheduleData::RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline,
|
||||
RescheduleData::RescheduleData(
|
||||
FileRequestPtr target,
|
||||
AZStd::chrono::system_clock::time_point newDeadline,
|
||||
IStreamerTypes::Priority newPriority)
|
||||
: m_target(AZStd::move(target))
|
||||
, m_newDeadline(newDeadline)
|
||||
, m_newPriority(newPriority)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::CreateDedicatedCacheData::CreateDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{}
|
||||
|
||||
FileRequest::DestroyDedicatedCacheData::DestroyDedicatedCacheData(RequestPath path, const FileRange& range)
|
||||
: m_path(AZStd::move(path))
|
||||
, m_range(range)
|
||||
{}
|
||||
|
||||
FileRequest::ReportData::ReportData(ReportType reportType)
|
||||
ReportData::ReportData(ReportType reportType)
|
||||
: m_reportType(reportType)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
FileRequest::CustomData::CustomData(AZStd::any data, bool failWhenUnhandled)
|
||||
CustomData::CustomData(AZStd::any data, bool failWhenUnhandled)
|
||||
: m_data(AZStd::move(data))
|
||||
, m_failWhenUnhandled(failWhenUnhandled)
|
||||
{}
|
||||
|
||||
{
|
||||
}
|
||||
} // namespace AZ::IO::Requests
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
//
|
||||
// FileRequest
|
||||
//
|
||||
@@ -145,14 +175,14 @@ namespace AZ::IO
|
||||
m_parent = request->m_request.m_parent;
|
||||
request->m_request.m_parent = this;
|
||||
m_dependencies++;
|
||||
m_command.emplace<ExternalRequestData>(AZStd::move(request));
|
||||
m_command.emplace<Requests::ExternalRequestData>(AZStd::move(request));
|
||||
}
|
||||
|
||||
void FileRequest::CreateRequestPathStore(FileRequest* parent, RequestPath path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CreateRequestPathStore', but another task was already assigned.");
|
||||
m_command.emplace<RequestPathStoreData>(AZStd::move(path));
|
||||
m_command.emplace<Requests::RequestPathStoreData>(AZStd::move(path));
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
@@ -161,7 +191,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'ReadRequest', but another task was already assigned.");
|
||||
m_command.emplace<ReadRequestData>(AZStd::move(path), output, outputSize, offset, size, deadline, priority);
|
||||
m_command.emplace<Requests::ReadRequestData>(AZStd::move(path), output, outputSize, offset, size, deadline, priority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReadRequest(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
@@ -169,7 +199,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'ReadRequest', but another task was already assigned.");
|
||||
m_command.emplace<ReadRequestData>(AZStd::move(path), allocator, offset, size, deadline, priority);
|
||||
m_command.emplace<Requests::ReadRequestData>(AZStd::move(path), allocator, offset, size, deadline, priority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateRead(FileRequest* parent, void* output, u64 outputSize, const RequestPath& path,
|
||||
@@ -177,7 +207,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Read', but another task was already assigned.");
|
||||
m_command.emplace<ReadData>(output, outputSize, AZStd::move(path), offset, size, sharedRead);
|
||||
m_command.emplace<Requests::ReadData>(output, outputSize, AZStd::move(path), offset, size, sharedRead);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
@@ -192,7 +222,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CompressedRead', but another task was already assigned.");
|
||||
m_command.emplace<CompressedReadData>(AZStd::move(compressionInfo), output, readOffset, readSize);
|
||||
m_command.emplace<Requests::CompressedReadData>(AZStd::move(compressionInfo), output, readOffset, readSize);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
@@ -200,7 +230,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Wait', but another task was already assigned.");
|
||||
m_command.emplace<WaitData>();
|
||||
m_command.emplace<Requests::WaitData>();
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
@@ -208,21 +238,21 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FileExistsCheck', but another task was already assigned.");
|
||||
m_command.emplace<FileExistsCheckData>(path);
|
||||
m_command.emplace<Requests::FileExistsCheckData>(path);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFileMetaDataRetrieval(const RequestPath& path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FileMetaDataRetrieval', but another task was already assigned.");
|
||||
m_command.emplace<FileMetaDataRetrievalData>(path);
|
||||
m_command.emplace<Requests::FileMetaDataRetrievalData>(path);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCancel(FileRequestPtr target)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Cancel', but another task was already assigned.");
|
||||
m_command.emplace<CancelData>(AZStd::move(target));
|
||||
m_command.emplace<Requests::CancelData>(AZStd::move(target));
|
||||
}
|
||||
|
||||
void FileRequest::CreateReschedule(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline,
|
||||
@@ -230,28 +260,28 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Reschedule', but another task was already assigned.");
|
||||
m_command.emplace<RescheduleData>(AZStd::move(target), newDeadline, newPriority);
|
||||
m_command.emplace<Requests::RescheduleData>(AZStd::move(target), newDeadline, newPriority);
|
||||
}
|
||||
|
||||
void FileRequest::CreateFlush(RequestPath path)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Flush', but another task was already assigned.");
|
||||
m_command.emplace<FlushData>(AZStd::move(path));
|
||||
m_command.emplace<Requests::FlushData>(AZStd::move(path));
|
||||
}
|
||||
|
||||
void FileRequest::CreateFlushAll()
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'FlushAll', but another task was already assigned.");
|
||||
m_command.emplace<FlushAllData>();
|
||||
m_command.emplace<Requests::FlushAllData>();
|
||||
}
|
||||
|
||||
void FileRequest::CreateDedicatedCacheCreation(RequestPath path, const FileRange& range, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'CreateDedicateCache', but another task was already assigned.");
|
||||
m_command.emplace<CreateDedicatedCacheData>(AZStd::move(path), range);
|
||||
m_command.emplace<Requests::CreateDedicatedCacheData>(AZStd::move(path), range);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
@@ -259,22 +289,22 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'DestroyDedicateCache', but another task was already assigned.");
|
||||
m_command.emplace<DestroyDedicatedCacheData>(AZStd::move(path), range);
|
||||
m_command.emplace<Requests::DestroyDedicatedCacheData>(AZStd::move(path), range);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
void FileRequest::CreateReport(ReportData::ReportType reportType)
|
||||
void FileRequest::CreateReport(Requests::ReportType reportType)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Report', but another task was already assigned.");
|
||||
m_command.emplace<ReportData>(reportType);
|
||||
m_command.emplace<Requests::ReportData>(reportType);
|
||||
}
|
||||
|
||||
void FileRequest::CreateCustom(AZStd::any data, bool failWhenUnhandled, FileRequest* parent)
|
||||
{
|
||||
AZ_Assert(AZStd::holds_alternative<AZStd::monostate>(m_command),
|
||||
"Attempting to set FileRequest to 'Custom', but another task was already assigned.");
|
||||
m_command.emplace<CustomData>(AZStd::move(data), failWhenUnhandled);
|
||||
m_command.emplace<Requests::CustomData>(AZStd::move(data), failWhenUnhandled);
|
||||
SetOptionalParent(parent);
|
||||
}
|
||||
|
||||
@@ -361,7 +391,7 @@ namespace AZ::IO
|
||||
"Request does not contain a valid command. It may have been reset already or was never assigned a command.");
|
||||
return true;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CustomData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CustomData>)
|
||||
{
|
||||
return args.m_failWhenUnhandled;
|
||||
}
|
||||
@@ -398,7 +428,7 @@ namespace AZ::IO
|
||||
const FileRequest* current = this;
|
||||
while (current)
|
||||
{
|
||||
auto* link = AZStd::get_if<ExternalRequestData>(¤t->m_command);
|
||||
auto* link = AZStd::get_if<Requests::ExternalRequestData>(¤t->m_command);
|
||||
if (!link)
|
||||
{
|
||||
current = current->m_parent;
|
||||
|
||||
@@ -27,7 +27,252 @@ namespace AZ::IO
|
||||
class ExternalFileRequest;
|
||||
|
||||
using FileRequestPtr = AZStd::intrusive_ptr<ExternalFileRequest>;
|
||||
} // namespace AZ::IO
|
||||
|
||||
namespace AZ::IO::Requests
|
||||
{
|
||||
//! Request to read data. This is a translated request and holds an absolute path and has been
|
||||
//! resolved to the archive file if needed.
|
||||
struct ReadData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead);
|
||||
|
||||
const RequestPath& m_path; //!< The path to the file that contains the requested data.
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock.
|
||||
};
|
||||
|
||||
//! Request to read data. This is an untranslated request and holds a relative path. The Scheduler
|
||||
//! will translate this to the appropriate ReadData or CompressedReadData.
|
||||
struct ReadRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
ReadRequestData(
|
||||
RequestPath path,
|
||||
void* output,
|
||||
u64 outputSize,
|
||||
u64 offset,
|
||||
u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline,
|
||||
IStreamerTypes::Priority priority);
|
||||
ReadRequestData(
|
||||
RequestPath path,
|
||||
IStreamerTypes::RequestMemoryAllocator* allocator,
|
||||
u64 offset,
|
||||
u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline,
|
||||
IStreamerTypes::Priority priority);
|
||||
~ReadRequestData();
|
||||
|
||||
RequestPath m_path; //!< Relative path to the target file.
|
||||
IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request.
|
||||
AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed.
|
||||
void* m_output; //!< The memory address assigned (during processing) to store the read data to.
|
||||
u64 m_outputSize; //!< The memory size of the addressed used to store the read data.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline.
|
||||
IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used.
|
||||
};
|
||||
|
||||
//! Creates a cache dedicated to a single file. This is best used for files where blocks are read from
|
||||
//! periodically such as audio banks of video files.
|
||||
struct CreateDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
CreateDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
//! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache
|
||||
struct DestroyDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
DestroyDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
enum class ReportType : int8_t
|
||||
{
|
||||
FileLocks
|
||||
};
|
||||
|
||||
struct ReportData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit ReportData(ReportType reportType);
|
||||
|
||||
ReportType m_reportType;
|
||||
};
|
||||
|
||||
//! Stores a reference to the external request so it stays alive while the request is being processed.
|
||||
//! This is needed because Streamer supports fire-and-forget requests since completion can be handled by
|
||||
//! registering a callback.
|
||||
struct ExternalRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
explicit ExternalRequestData(FileRequestPtr&& request);
|
||||
|
||||
FileRequestPtr m_request; //!< The request that was send to Streamer.
|
||||
};
|
||||
|
||||
//! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that
|
||||
//! need a path take them by reference to the original request. In some cases a path originates from
|
||||
//! within in the stack and temporary storage is needed. This struct allows for that temporary storage
|
||||
//! so it can be safely referenced later.
|
||||
struct RequestPathStoreData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
explicit RequestPathStoreData(RequestPath path);
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
//! Request to read and decompress data.
|
||||
struct CompressedReadData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize);
|
||||
|
||||
CompressionInfo m_compressionInfo;
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_readOffset; //!< The offset into the decompressed to start copying from.
|
||||
u64 m_readSize; //!< Number of bytes to read from the decompressed file.
|
||||
};
|
||||
|
||||
//! Holds the progress of an operation chain until this request is explicitly completed.
|
||||
struct WaitData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
};
|
||||
|
||||
//! Checks to see if any node in the stack can find a file at the provided path.
|
||||
struct FileExistsCheckData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileExistsCheckData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists
|
||||
//! check.
|
||||
struct FileMetaDataRetrievalData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileMetaDataRetrievalData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
u64 m_fileSize{ 0 };
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Cancels a request in the stream stack, if possible.
|
||||
struct CancelData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit CancelData(FileRequestPtr target);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be canceled.
|
||||
};
|
||||
|
||||
//! Updates the priority and deadline of a request that has not been queued yet.
|
||||
struct RescheduleData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be rescheduled.
|
||||
AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request.
|
||||
IStreamerTypes::Priority m_newPriority; //!< The new priority for the request.
|
||||
};
|
||||
|
||||
//! Flushes all references to the provided file in the streaming stack.
|
||||
struct FlushData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FlushData(RequestPath path);
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
//! Flushes all caches in the streaming stack.
|
||||
struct FlushAllData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
};
|
||||
|
||||
//! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored
|
||||
//! in the already provided data.
|
||||
struct CustomData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
|
||||
CustomData(AZStd::any data, bool failWhenUnhandled);
|
||||
|
||||
AZStd::any m_data; //!< The data for the custom request.
|
||||
bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it.
|
||||
};
|
||||
using CommandVariant = AZStd::variant<
|
||||
AZStd::monostate,
|
||||
ExternalRequestData,
|
||||
RequestPathStoreData,
|
||||
ReadRequestData,
|
||||
ReadData,
|
||||
CompressedReadData,
|
||||
WaitData,
|
||||
FileExistsCheckData,
|
||||
FileMetaDataRetrievalData,
|
||||
CancelData,
|
||||
RescheduleData,
|
||||
FlushData,
|
||||
FlushAllData,
|
||||
CreateDedicatedCacheData,
|
||||
DestroyDedicatedCacheData,
|
||||
ReportData,
|
||||
CustomData>;
|
||||
|
||||
} // namespace AZ::IO::Requests
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
class FileRequest final
|
||||
{
|
||||
public:
|
||||
@@ -36,218 +281,7 @@ namespace AZ::IO
|
||||
friend class StreamerContext;
|
||||
friend class ExternalFileRequest;
|
||||
|
||||
//! Stores a reference to the external request so it stays alive while the request is being processed.
|
||||
//! This is needed because Streamer supports fire-and-forget requests since completion can be handled by
|
||||
//! registering a callback.
|
||||
struct ExternalRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
explicit ExternalRequestData(FileRequestPtr&& request);
|
||||
|
||||
FileRequestPtr m_request; //!< The request that was send to Streamer.
|
||||
};
|
||||
|
||||
//! Stores an instance of a RequestPath. To reduce copying instances of a RequestPath functions that
|
||||
//! need a path take them by reference to the original request. In some cases a path originates from
|
||||
//! within in the stack and temporary storage is needed. This struct allows for that temporary storage
|
||||
//! so it can be safely referenced later.
|
||||
struct RequestPathStoreData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
explicit RequestPathStoreData(RequestPath path);
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
//! Request to read data. This is an untranslated request and holds a relative path. The Scheduler
|
||||
//! will translate this to the appropriate ReadData or CompressedReadData.
|
||||
struct ReadRequestData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
ReadRequestData(RequestPath path, void* output, u64 outputSize, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
ReadRequestData(RequestPath path, IStreamerTypes::RequestMemoryAllocator* allocator, u64 offset, u64 size,
|
||||
AZStd::chrono::system_clock::time_point deadline, IStreamerTypes::Priority priority);
|
||||
~ReadRequestData();
|
||||
|
||||
RequestPath m_path; //!< Relative path to the target file.
|
||||
IStreamerTypes::RequestMemoryAllocator* m_allocator; //!< Allocator used to manage the memory for this request.
|
||||
AZStd::chrono::system_clock::time_point m_deadline; //!< Time by which this request should have been completed.
|
||||
void* m_output; //!< The memory address assigned (during processing) to store the read data to.
|
||||
u64 m_outputSize; //!< The memory size of the addressed used to store the read data.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
IStreamerTypes::Priority m_priority; //!< Priority used for ordering requests. This is used when requests have the same deadline.
|
||||
IStreamerTypes::MemoryType m_memoryType; //!< The type of memory provided by the allocator if used.
|
||||
};
|
||||
|
||||
//! Request to read data. This is a translated request and holds an absolute path and has been
|
||||
//! resolved to the archive file if needed.
|
||||
struct ReadData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
ReadData(void* output, u64 outputSize, const RequestPath& path, u64 offset, u64 size, bool sharedRead);
|
||||
|
||||
const RequestPath& m_path; //!< The path to the file that contains the requested data.
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_outputSize; //!< Size of memory m_output points to. This needs to be at least as big as m_size, but can be bigger.
|
||||
u64 m_offset; //!< The offset in bytes into the file.
|
||||
u64 m_size; //!< The number of bytes to read from the file.
|
||||
bool m_sharedRead; //!< True if other code will be reading from the file or the stack entry can exclusively lock.
|
||||
};
|
||||
|
||||
//! Request to read and decompress data.
|
||||
struct CompressedReadData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
|
||||
CompressedReadData(CompressionInfo&& compressionInfo, void* output, u64 readOffset, u64 readSize);
|
||||
|
||||
CompressionInfo m_compressionInfo;
|
||||
void* m_output; //!< Target output to write the read data to.
|
||||
u64 m_readOffset; //!< The offset into the decompressed to start copying from.
|
||||
u64 m_readSize; //!< Number of bytes to read from the decompressed file.
|
||||
};
|
||||
|
||||
//! Holds the progress of an operation chain until this request is explicitly completed.
|
||||
struct WaitData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
inline constexpr static bool s_failWhenUnhandled = true;
|
||||
};
|
||||
|
||||
//! Checks to see if any node in the stack can find a file at the provided path.
|
||||
struct FileExistsCheckData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileExistsCheckData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Searches for a file in the stack and retrieves the meta data. This may be slower than a file exists
|
||||
//! check.
|
||||
struct FileMetaDataRetrievalData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FileMetaDataRetrievalData(const RequestPath& path);
|
||||
|
||||
const RequestPath& m_path;
|
||||
u64 m_fileSize{ 0 };
|
||||
bool m_found{ false };
|
||||
};
|
||||
|
||||
//! Cancels a request in the stream stack, if possible.
|
||||
struct CancelData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHighest;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit CancelData(FileRequestPtr target);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be canceled.
|
||||
};
|
||||
|
||||
//! Updates the priority and deadline of a request that has not been queued yet.
|
||||
struct RescheduleData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
RescheduleData(FileRequestPtr target, AZStd::chrono::system_clock::time_point newDeadline, IStreamerTypes::Priority newPriority);
|
||||
|
||||
FileRequestPtr m_target; //!< The request that will be rescheduled.
|
||||
AZStd::chrono::system_clock::time_point m_newDeadline; //!< The new deadline for the request.
|
||||
IStreamerTypes::Priority m_newPriority; //!< The new priority for the request.
|
||||
};
|
||||
|
||||
//! Flushes all references to the provided file in the streaming stack.
|
||||
struct FlushData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
explicit FlushData(RequestPath path);
|
||||
|
||||
RequestPath m_path;
|
||||
};
|
||||
|
||||
//! Flushes all caches in the streaming stack.
|
||||
struct FlushAllData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
};
|
||||
|
||||
//! Creates a cache dedicated to a single file. This is best used for files where blocks are read from
|
||||
//! periodically such as audio banks of video files.
|
||||
struct CreateDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
CreateDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
//! Destroys a cache dedicated to a single file that was previously created by CreateDedicatedCache
|
||||
struct DestroyDedicatedCacheData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityHigh;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
DestroyDedicatedCacheData(RequestPath path, const FileRange& range);
|
||||
|
||||
RequestPath m_path;
|
||||
FileRange m_range;
|
||||
};
|
||||
|
||||
struct ReportData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityLow;
|
||||
inline constexpr static bool s_failWhenUnhandled = false;
|
||||
|
||||
enum class ReportType
|
||||
{
|
||||
FileLocks
|
||||
};
|
||||
|
||||
explicit ReportData(ReportType reportType);
|
||||
|
||||
ReportType m_reportType;
|
||||
};
|
||||
|
||||
//! Data for a custom command. This can be used by nodes added extensions that need data that can't be stored
|
||||
//! in the already provided data.
|
||||
struct CustomData
|
||||
{
|
||||
inline constexpr static IStreamerTypes::Priority s_orderPriority = IStreamerTypes::s_priorityMedium;
|
||||
|
||||
CustomData(AZStd::any data, bool failWhenUnhandled);
|
||||
|
||||
AZStd::any m_data; //!< The data for the custom request.
|
||||
bool m_failWhenUnhandled; //!< Whether or not the request is marked as failed or success when no node process it.
|
||||
};
|
||||
|
||||
using CommandVariant = AZStd::variant<AZStd::monostate, ExternalRequestData, RequestPathStoreData, ReadRequestData, ReadData,
|
||||
CompressedReadData, WaitData, FileExistsCheckData, FileMetaDataRetrievalData, CancelData, RescheduleData, FlushData,
|
||||
FlushAllData, CreateDedicatedCacheData, DestroyDedicatedCacheData, ReportData, CustomData>;
|
||||
using CommandVariant = Requests::CommandVariant;
|
||||
using OnCompletionCallback = AZStd::function<void(FileRequest& request)>;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(FileRequest, SystemAllocator, 0);
|
||||
@@ -278,7 +312,7 @@ namespace AZ::IO
|
||||
void CreateFlushAll();
|
||||
void CreateDedicatedCacheCreation(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr);
|
||||
void CreateDedicatedCacheDestruction(RequestPath path, const FileRange& range = {}, FileRequest* parent = nullptr);
|
||||
void CreateReport(ReportData::ReportType reportType);
|
||||
void CreateReport(Requests::ReportType reportType);
|
||||
void CreateCustom(AZStd::any data, bool failWhenUnhandled = true, FileRequest* parent = nullptr);
|
||||
|
||||
void SetCompletionCallback(OnCompletionCallback callback);
|
||||
@@ -325,8 +359,17 @@ namespace AZ::IO
|
||||
//! Command and parameters for the request.
|
||||
CommandVariant m_command;
|
||||
|
||||
//! Status of the request.
|
||||
AZStd::atomic<IStreamerTypes::RequestStatus> m_status{ IStreamerTypes::RequestStatus::Pending };
|
||||
//! Estimated time this request will complete. This is an estimation and depends on many
|
||||
//! factors which can cause it to change drastically from moment to moment.
|
||||
AZStd::chrono::system_clock::time_point m_estimatedCompletion;
|
||||
|
||||
//! The file request that has a dependency on this one. This can be null if there are no
|
||||
//! other request depending on this one to complete.
|
||||
FileRequest* m_parent{ nullptr };
|
||||
|
||||
|
||||
//! Id assigned when the request is added to the pending queue.
|
||||
size_t m_pendingId{ 0 };
|
||||
|
||||
//! Called once the request has completed. This will always be called from the Streamer thread
|
||||
//! and thread safety is the responsibility of called function. When assigning a lambda avoid
|
||||
@@ -336,16 +379,8 @@ namespace AZ::IO
|
||||
//! a longer running task is needed consider using a job to do the work.
|
||||
OnCompletionCallback m_onCompletion;
|
||||
|
||||
//! Estimated time this request will complete. This is an estimation and depends on many
|
||||
//! factors which can cause it to change drastically from moment to moment.
|
||||
AZStd::chrono::system_clock::time_point m_estimatedCompletion;
|
||||
|
||||
//! The file request that has a dependency on this one. This can be null if there are no
|
||||
//! other request depending on this one to complete.
|
||||
FileRequest* m_parent{ nullptr };
|
||||
|
||||
//! Id assigned when the request is added to the pending queue.
|
||||
size_t m_pendingId{ 0 };
|
||||
//! Status of the request.
|
||||
AZStd::atomic<IStreamerTypes::RequestStatus> m_status{ IStreamerTypes::RequestStatus::Pending };
|
||||
|
||||
//! The number of dependent file request that need to complete before this one is done.
|
||||
u16 m_dependencies{ 0 };
|
||||
|
||||
@@ -91,12 +91,12 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadRequestData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadRequestData>)
|
||||
{
|
||||
PrepareReadRequest(request, args);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CreateDedicatedCacheData> ||
|
||||
AZStd::is_same_v<Command, Requests::DestroyDedicatedCacheData>)
|
||||
{
|
||||
PrepareDedicatedCache(request, args.m_path);
|
||||
}
|
||||
@@ -114,11 +114,11 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
m_pendingReads.push_back(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileExistsCheckData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FileExistsCheckData>)
|
||||
{
|
||||
m_pendingFileExistChecks.push_back(request);
|
||||
}
|
||||
@@ -203,7 +203,7 @@ namespace AZ::IO
|
||||
{
|
||||
FileRequest* compressedRequest = m_processingJobs[i].m_waitRequest->GetParent();
|
||||
AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request.");
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
AZ_Assert(data, "Compressed request in the decompression queue in FullFileDecompressor didn't contain compression read data.");
|
||||
|
||||
size_t bytesToDecompress = data->m_compressionInfo.m_compressedSize;
|
||||
@@ -255,7 +255,7 @@ namespace AZ::IO
|
||||
|
||||
// Calculate the amount of time it will take to decompress the data.
|
||||
FileRequest* compressedRequest = m_readRequests[i]->GetParent();
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
|
||||
size_t bytesToDecompress = data->m_compressionInfo.m_compressedSize;
|
||||
auto decompressionDuration = AZStd::chrono::microseconds(
|
||||
@@ -290,7 +290,7 @@ namespace AZ::IO
|
||||
void FullFileDecompressor::EstimateCompressedReadRequest(FileRequest* request, AZStd::chrono::microseconds& cumulativeDelay,
|
||||
AZStd::chrono::microseconds decompressionDelay, double totalDecompressionDurationUs, double totalBytesDecompressed) const
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&request->GetCommand());
|
||||
if (data)
|
||||
{
|
||||
AZStd::chrono::microseconds processingTime = decompressionDelay;
|
||||
@@ -343,7 +343,7 @@ namespace AZ::IO
|
||||
m_numRunningJobs == 0;
|
||||
}
|
||||
|
||||
void FullFileDecompressor::PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data)
|
||||
void FullFileDecompressor::PrepareReadRequest(FileRequest* request, Requests::ReadRequestData& data)
|
||||
{
|
||||
CompressionInfo info;
|
||||
if (CompressionUtils::FindCompressionInfo(info, data.m_path.GetRelativePath()))
|
||||
@@ -359,7 +359,7 @@ namespace AZ::IO
|
||||
{
|
||||
FileRequest* pathStorageRequest = m_context->GetNewInternalRequest();
|
||||
pathStorageRequest->CreateRequestPathStore(request, AZStd::move(info.m_archiveFilename));
|
||||
auto& pathStorage = AZStd::get<FileRequest::RequestPathStoreData>(pathStorageRequest->GetCommand());
|
||||
auto& pathStorage = AZStd::get<Requests::RequestPathStoreData>(pathStorageRequest->GetCommand());
|
||||
|
||||
nextRequest->CreateRead(pathStorageRequest, data.m_output, data.m_outputSize, pathStorage.m_path,
|
||||
info.m_offset + data.m_offset, data.m_size, info.m_isSharedPak);
|
||||
@@ -370,13 +370,13 @@ namespace AZ::IO
|
||||
auto callback = [this, nextRequest](const FileRequest& checkRequest)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
auto check = AZStd::get_if<FileRequest::FileExistsCheckData>(&checkRequest.GetCommand());
|
||||
auto check = AZStd::get_if<Requests::FileExistsCheckData>(&checkRequest.GetCommand());
|
||||
AZ_Assert(check,
|
||||
"Callback in FullFileDecompressor::PrepareReadRequest expected FileExistsCheck but got another command.");
|
||||
if (check->m_found)
|
||||
{
|
||||
FileRequest* originalRequest = m_context->RejectRequest(nextRequest);
|
||||
if (AZStd::holds_alternative<FileRequest::RequestPathStoreData>(originalRequest->GetCommand()))
|
||||
if (AZStd::holds_alternative<Requests::RequestPathStoreData>(originalRequest->GetCommand()))
|
||||
{
|
||||
originalRequest = m_context->RejectRequest(originalRequest);
|
||||
}
|
||||
@@ -412,12 +412,12 @@ namespace AZ::IO
|
||||
AZStd::visit([request, &info, nextRequest](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::CreateDedicatedCacheData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::CreateDedicatedCacheData>)
|
||||
{
|
||||
nextRequest->CreateDedicatedCacheCreation(AZStd::move(info.m_archiveFilename),
|
||||
FileRange::CreateRange(info.m_offset, info.m_compressedSize), request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::DestroyDedicatedCacheData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::DestroyDedicatedCacheData>)
|
||||
{
|
||||
nextRequest->CreateDedicatedCacheDestruction(AZStd::move(info.m_archiveFilename),
|
||||
FileRange::CreateRange(info.m_offset, info.m_compressedSize), request);
|
||||
@@ -429,7 +429,7 @@ namespace AZ::IO
|
||||
auto callback = [this, nextRequest](const FileRequest& checkRequest)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
auto check = AZStd::get_if<FileRequest::FileExistsCheckData>(&checkRequest.GetCommand());
|
||||
auto check = AZStd::get_if<Requests::FileExistsCheckData>(&checkRequest.GetCommand());
|
||||
AZ_Assert(check,
|
||||
"Callback in FullFileDecompressor::PrepareDedicatedCache expected FileExistsCheck but got another command.");
|
||||
if (check->m_found)
|
||||
@@ -461,7 +461,7 @@ namespace AZ::IO
|
||||
|
||||
void FullFileDecompressor::FileExistsCheck(FileRequest* checkRequest)
|
||||
{
|
||||
auto& fileCheckRequest = AZStd::get<FileRequest::FileExistsCheckData>(checkRequest->GetCommand());
|
||||
auto& fileCheckRequest = AZStd::get<Requests::FileExistsCheckData>(checkRequest->GetCommand());
|
||||
CompressionInfo info;
|
||||
if (CompressionUtils::FindCompressionInfo(info, fileCheckRequest.m_path.GetRelativePath()))
|
||||
{
|
||||
@@ -487,7 +487,7 @@ namespace AZ::IO
|
||||
{
|
||||
if (m_readBufferStatus[i] == ReadBufferStatus::Unused)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&compressedReadRequest->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&compressedReadRequest->GetCommand());
|
||||
AZ_Assert(data, "Compressed request that's starting a read in FullFileDecompressor didn't contain compression read data.");
|
||||
AZ_Assert(data->m_compressionInfo.m_decompressor,
|
||||
"FileRequest for FullFileDecompressor is missing a decompression callback.");
|
||||
@@ -549,7 +549,7 @@ namespace AZ::IO
|
||||
}
|
||||
else
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
AZ_Assert(data, "Compressed request in FullFileDecompressor that finished unsuccessfully didn't contain compression read data.");
|
||||
CompressionInfo& info = data->m_compressionInfo;
|
||||
size_t offsetAdjustment = info.m_offset - AZ_SIZE_ALIGN_DOWN(info.m_offset, aznumeric_cast<size_t>(m_alignment));
|
||||
@@ -591,7 +591,7 @@ namespace AZ::IO
|
||||
}
|
||||
|
||||
FileRequest* waitRequest = m_readRequests[readSlot];
|
||||
AZ_Assert(AZStd::holds_alternative<FileRequest::WaitData>(waitRequest->GetCommand()),
|
||||
AZ_Assert(AZStd::holds_alternative<Requests::WaitData>(waitRequest->GetCommand()),
|
||||
"File request waiting for decompression wasn't marked as being a wait operation.");
|
||||
FileRequest* compressedRequest = waitRequest->GetParent();
|
||||
AZ_Assert(compressedRequest, "Read requests started by FullFileDecompressor is missing a parent request.");
|
||||
@@ -610,7 +610,7 @@ namespace AZ::IO
|
||||
m_readBuffers[readSlot] = nullptr;
|
||||
|
||||
AZ::Job* decompressionJob;
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
AZ_Assert(data, "Compressed request in FullFileDecompressor that's starting decompression didn't contain compression read data.");
|
||||
AZ_Assert(data->m_compressionInfo.m_decompressor, "FullFileDecompressor is queuing a decompression job but couldn't find a decompressor.");
|
||||
|
||||
@@ -664,7 +664,7 @@ namespace AZ::IO
|
||||
|
||||
FileRequest* compressedRequest = jobInfo.m_waitRequest->GetParent();
|
||||
AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request.");
|
||||
auto data = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
AZ_Assert(data, "Compressed request in FullFileDecompressor that completed decompression didn't contain compression read data.");
|
||||
CompressionInfo& info = data->m_compressionInfo;
|
||||
size_t offsetAdjustment = info.m_offset - AZ_SIZE_ALIGN_DOWN(info.m_offset, aznumeric_cast<size_t>(m_alignment));
|
||||
@@ -694,7 +694,7 @@ namespace AZ::IO
|
||||
|
||||
FileRequest* compressedRequest = info.m_waitRequest->GetParent();
|
||||
AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request.");
|
||||
auto request = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto request = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
AZ_Assert(request, "Compressed request in FullFileDecompressor that's running full decompression didn't contain compression read data.");
|
||||
CompressionInfo& compressionInfo = request->m_compressionInfo;
|
||||
AZ_Assert(compressionInfo.m_decompressor, "Full decompressor job started, but there's no decompressor callback assigned.");
|
||||
@@ -719,7 +719,7 @@ namespace AZ::IO
|
||||
|
||||
FileRequest* compressedRequest = info.m_waitRequest->GetParent();
|
||||
AZ_Assert(compressedRequest, "A wait request attached to FullFileDecompressor was completed but didn't have a parent compressed request.");
|
||||
auto request = AZStd::get_if<FileRequest::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
auto request = AZStd::get_if<Requests::CompressedReadData>(&compressedRequest->GetCommand());
|
||||
AZ_Assert(request, "Compressed request in FullFileDecompressor that's running partial decompression didn't contain compression read data.");
|
||||
CompressionInfo& compressionInfo = request->m_compressionInfo;
|
||||
AZ_Assert(compressionInfo.m_decompressor, "Partial decompressor job started, but there's no decompressor callback assigned.");
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
namespace Requests
|
||||
{
|
||||
struct ReadRequestData;
|
||||
}
|
||||
|
||||
struct FullFileDecompressorConfig final :
|
||||
public IStreamerStackConfig
|
||||
{
|
||||
@@ -87,7 +92,7 @@ namespace AZ::IO
|
||||
|
||||
bool IsIdle() const;
|
||||
|
||||
void PrepareReadRequest(FileRequest* request, FileRequest::ReadRequestData& data);
|
||||
void PrepareReadRequest(FileRequest* request, Requests::ReadRequestData& data);
|
||||
void PrepareDedicatedCache(FileRequest* request, const RequestPath& path);
|
||||
void FileExistsCheck(FileRequest* checkRequest);
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace AZ::IO
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&request->GetCommand());
|
||||
if (data == nullptr)
|
||||
{
|
||||
StreamStackEntry::QueueRequest(request);
|
||||
@@ -156,7 +156,7 @@ namespace AZ::IO
|
||||
|
||||
void ReadSplitter::QueueAlignedRead(FileRequest* request)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
if (data->m_size <= m_maxReadSize)
|
||||
@@ -187,7 +187,7 @@ namespace AZ::IO
|
||||
|
||||
bool ReadSplitter::QueueAlignedRead(PendingRead& pending)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&pending.m_request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&pending.m_request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
while (pending.m_readSize > 0)
|
||||
@@ -237,7 +237,7 @@ namespace AZ::IO
|
||||
|
||||
void ReadSplitter::QueueBufferedRead(FileRequest* request)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
PendingRead pendingRead;
|
||||
@@ -262,7 +262,7 @@ namespace AZ::IO
|
||||
|
||||
bool ReadSplitter::QueueBufferedRead(PendingRead& pending)
|
||||
{
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&pending.m_request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&pending.m_request->GetCommand());
|
||||
AZ_Assert(data != nullptr, "Provided request to queue by the Read Splitter did not contain a read command.");
|
||||
|
||||
while (pending.m_readSize > 0)
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/IO/Streamer/Scheduler.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/IO/Streamer/Scheduler.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
|
||||
@@ -35,6 +37,8 @@ namespace AZ::IO
|
||||
m_threadData.m_streamStack = AZStd::move(streamStack);
|
||||
}
|
||||
|
||||
Scheduler::~Scheduler() = default;
|
||||
|
||||
void Scheduler::Start(const AZStd::thread_desc& threadDesc)
|
||||
{
|
||||
if (!m_isRunning)
|
||||
@@ -222,10 +226,10 @@ namespace AZ::IO
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (
|
||||
AZStd::is_same_v<Command, FileRequest::ReadData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
AZStd::is_same_v<Command, Requests::ReadData> ||
|
||||
AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
auto parentReadRequest = next->GetCommandFromChain<FileRequest::ReadRequestData>();
|
||||
auto parentReadRequest = next->GetCommandFromChain<Requests::ReadRequestData>();
|
||||
AZ_Assert(parentReadRequest != nullptr, "The issued read request can't be found for the (compressed) read command.");
|
||||
|
||||
size_t size = parentReadRequest->m_size;
|
||||
@@ -234,7 +238,7 @@ namespace AZ::IO
|
||||
AZ_Assert(parentReadRequest->m_allocator,
|
||||
"The read request was issued without a memory allocator or valid output address.");
|
||||
u64 recommendedSize = size;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
recommendedSize = m_recommendations.CalculateRecommendedMemorySize(size, parentReadRequest->m_offset);
|
||||
}
|
||||
@@ -249,12 +253,12 @@ namespace AZ::IO
|
||||
parentReadRequest->m_output = allocation.m_address;
|
||||
parentReadRequest->m_outputSize = allocation.m_size;
|
||||
parentReadRequest->m_memoryType = allocation.m_type;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
args.m_output = parentReadRequest->m_output;
|
||||
args.m_outputSize = allocation.m_size;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
args.m_output = parentReadRequest->m_output;
|
||||
}
|
||||
@@ -267,7 +271,7 @@ namespace AZ::IO
|
||||
}
|
||||
#endif
|
||||
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
m_threadData.m_lastFilePath = args.m_path;
|
||||
m_threadData.m_lastFileOffset = args.m_offset + args.m_size;
|
||||
@@ -275,7 +279,7 @@ namespace AZ::IO
|
||||
m_processingSize += args.m_size;
|
||||
#endif
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
const CompressionInfo& info = args.m_compressionInfo;
|
||||
m_threadData.m_lastFilePath = info.m_archiveFilename;
|
||||
@@ -288,15 +292,15 @@ namespace AZ::IO
|
||||
"Streamer queued %zu: %s", next->GetCommand().index(), parentReadRequest->m_path.GetRelativePath());
|
||||
m_threadData.m_streamStack->QueueRequest(next);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CancelData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CancelData>)
|
||||
{
|
||||
return Thread_ProcessCancelRequest(next, args);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::RescheduleData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::RescheduleData>)
|
||||
{
|
||||
return Thread_ProcessRescheduleRequest(next, args);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData> || AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FlushData> || AZStd::is_same_v<Command, Requests::FlushAllData>)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, next, ProfilerColor,
|
||||
"Streamer queued %zu", next->GetCommand().index());
|
||||
@@ -345,7 +349,7 @@ namespace AZ::IO
|
||||
#endif
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadRequestData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadRequestData>)
|
||||
{
|
||||
if (args.m_output == nullptr && args.m_allocator != nullptr)
|
||||
{
|
||||
@@ -393,7 +397,7 @@ namespace AZ::IO
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data)
|
||||
void Scheduler::Thread_ProcessCancelRequest(FileRequest* request, Requests::CancelData& data)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, request, ProfilerColor, "Streamer queued cancel");
|
||||
auto& pending = m_context.GetPreparedRequests();
|
||||
@@ -415,7 +419,7 @@ namespace AZ::IO
|
||||
m_threadData.m_streamStack->QueueRequest(request);
|
||||
}
|
||||
|
||||
void Scheduler::Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data)
|
||||
void Scheduler::Thread_ProcessRescheduleRequest(FileRequest* request, Requests::RescheduleData& data)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_START_COLORED(AzCore, request, ProfilerColor, "Streamer queued reschedule");
|
||||
auto& pendingRequests = m_context.GetPreparedRequests();
|
||||
@@ -424,7 +428,7 @@ namespace AZ::IO
|
||||
if (pending->WorksOn(data.m_target))
|
||||
{
|
||||
// Read requests are the only requests that use deadlines and dynamic priorities.
|
||||
auto readRequest = pending->GetCommandFromChain<FileRequest::ReadRequestData>();
|
||||
auto readRequest = pending->GetCommandFromChain<Requests::ReadRequestData>();
|
||||
if (readRequest)
|
||||
{
|
||||
readRequest->m_deadline = data.m_newDeadline;
|
||||
@@ -463,8 +467,8 @@ namespace AZ::IO
|
||||
|
||||
// Order is the same for both requests, so prioritize the request that are at risk of missing
|
||||
// it's deadline.
|
||||
const FileRequest::ReadRequestData* firstRead = first->GetCommandFromChain<FileRequest::ReadRequestData>();
|
||||
const FileRequest::ReadRequestData* secondRead = second->GetCommandFromChain<FileRequest::ReadRequestData>();
|
||||
const Requests::ReadRequestData* firstRead = first->GetCommandFromChain<Requests::ReadRequestData>();
|
||||
const Requests::ReadRequestData* secondRead = second->GetCommandFromChain<Requests::ReadRequestData>();
|
||||
|
||||
if (firstRead == nullptr || secondRead == nullptr)
|
||||
{
|
||||
@@ -496,11 +500,11 @@ namespace AZ::IO
|
||||
auto sameFile = [this](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
return m_threadData.m_lastFilePath == args.m_path;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
return m_threadData.m_lastFilePath == args.m_compressionInfo.m_archiveFilename;
|
||||
}
|
||||
@@ -517,11 +521,11 @@ namespace AZ::IO
|
||||
auto offset = [](auto&& args) -> s64
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
return aznumeric_caster(args.m_offset);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
return aznumeric_caster(args.m_compressionInfo.m_offset);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/IO/Streamer/RequestPath.h>
|
||||
#include <AzCore/IO/IStreamerTypes.h>
|
||||
#include <AzCore/IO/Streamer/Statistics.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
@@ -24,11 +25,19 @@ namespace AZ::IO
|
||||
{
|
||||
class FileRequest;
|
||||
|
||||
namespace Requests
|
||||
{
|
||||
struct CancelData;
|
||||
struct RescheduleData;
|
||||
} // namespace Requests
|
||||
|
||||
class Scheduler final
|
||||
{
|
||||
public:
|
||||
explicit Scheduler(AZStd::shared_ptr<StreamStackEntry> streamStack, u64 memoryAlignment = AZCORE_GLOBAL_NEW_ALIGNMENT,
|
||||
u64 sizeAlignment = 1, u64 granularity = 1_mib);
|
||||
~Scheduler();
|
||||
|
||||
void Start(const AZStd::thread_desc& threadDesc);
|
||||
void Stop();
|
||||
|
||||
@@ -61,14 +70,14 @@ namespace AZ::IO
|
||||
bool Thread_ExecuteRequests();
|
||||
bool Thread_PrepareRequests(AZStd::vector<FileRequestPtr>& outstandingRequests);
|
||||
void Thread_ProcessTillIdle();
|
||||
void Thread_ProcessCancelRequest(FileRequest* request, FileRequest::CancelData& data);
|
||||
void Thread_ProcessRescheduleRequest(FileRequest* request, FileRequest::RescheduleData& data);
|
||||
void Thread_ProcessCancelRequest(FileRequest* request, Requests::CancelData& data);
|
||||
void Thread_ProcessRescheduleRequest(FileRequest* request, Requests::RescheduleData& data);
|
||||
|
||||
enum class Order
|
||||
{
|
||||
FirstRequest, //< The first request is the most important to process next.
|
||||
SecondRequest, //< The second request is the most important to process next.
|
||||
Equal //< Both requests are equally important.
|
||||
FirstRequest, //!< The first request is the most important to process next.
|
||||
SecondRequest, //!< The second request is the most important to process next.
|
||||
Equal //!< Both requests are equally important.
|
||||
};
|
||||
//! Determine which of the two provided requests is more important to process next.
|
||||
Order Thread_PrioritizeRequests(const FileRequest* first, const FileRequest* second) const;
|
||||
|
||||
@@ -60,13 +60,13 @@ namespace AZ::IO
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
AZ_Assert(request, "PrepareRequest was provided a null request.");
|
||||
|
||||
if (AZStd::holds_alternative<FileRequest::ReadRequestData>(request->GetCommand()))
|
||||
if (AZStd::holds_alternative<Requests::ReadRequestData>(request->GetCommand()))
|
||||
{
|
||||
auto& readRequest = AZStd::get<FileRequest::ReadRequestData>(request->GetCommand());
|
||||
auto& readRequest = AZStd::get<Requests::ReadRequestData>(request->GetCommand());
|
||||
|
||||
FileRequest* read = m_context->GetNewInternalRequest();
|
||||
read->CreateRead(request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path,
|
||||
readRequest.m_offset, readRequest.m_size);
|
||||
read->CreateRead(
|
||||
request, readRequest.m_output, readRequest.m_outputSize, readRequest.m_path, readRequest.m_offset, readRequest.m_size);
|
||||
m_context->PushPreparedRequest(read);
|
||||
return;
|
||||
}
|
||||
@@ -79,29 +79,29 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::FileExistsCheckData> ||
|
||||
AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData> ||
|
||||
AZStd::is_same_v<Command, Requests::FileExistsCheckData> ||
|
||||
AZStd::is_same_v<Command, Requests::FileMetaDataRetrievalData>)
|
||||
{
|
||||
m_pendingRequests.push_back(request);
|
||||
return;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CancelData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CancelData>)
|
||||
{
|
||||
CancelRequest(request, args.m_target);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::FlushData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::FlushData>)
|
||||
{
|
||||
FlushCache(args.m_path);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FlushAllData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FlushAllData>)
|
||||
{
|
||||
FlushEntireCache();
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::ReportData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::ReportData>)
|
||||
{
|
||||
Report(args);
|
||||
}
|
||||
@@ -118,15 +118,15 @@ namespace AZ::IO
|
||||
AZStd::visit([this, request](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
ReadFile(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileExistsCheckData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FileExistsCheckData>)
|
||||
{
|
||||
FileExistsRequest(request);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FileMetaDataRetrievalData>)
|
||||
{
|
||||
FileMetaDataRetrievalRequest(request);
|
||||
}
|
||||
@@ -199,25 +199,25 @@ namespace AZ::IO
|
||||
AZStd::visit([&](auto&& args)
|
||||
{
|
||||
using Command = AZStd::decay_t<decltype(args)>;
|
||||
if constexpr (AZStd::is_same_v<Command, FileRequest::ReadData>)
|
||||
if constexpr (AZStd::is_same_v<Command, Requests::ReadData>)
|
||||
{
|
||||
targetFile = &args.m_path;
|
||||
readSize = args.m_size;
|
||||
offset = args.m_offset;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::CompressedReadData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::CompressedReadData>)
|
||||
{
|
||||
targetFile = &args.m_compressionInfo.m_archiveFilename;
|
||||
readSize = args.m_compressionInfo.m_compressedSize;
|
||||
offset = args.m_compressionInfo.m_offset;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileExistsCheckData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FileExistsCheckData>)
|
||||
{
|
||||
readSize = 0;
|
||||
AZStd::chrono::microseconds averageTime = m_getFileExistsTimeAverage.CalculateAverage();
|
||||
startTime += averageTime;
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Command, FileRequest::FileMetaDataRetrievalData>)
|
||||
else if constexpr (AZStd::is_same_v<Command, Requests::FileMetaDataRetrievalData>)
|
||||
{
|
||||
readSize = 0;
|
||||
AZStd::chrono::microseconds averageTime = m_getFileMetaDataTimeAverage.CalculateAverage();
|
||||
@@ -254,7 +254,7 @@ namespace AZ::IO
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
|
||||
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
|
||||
auto data = AZStd::get_if<Requests::ReadData>(&request->GetCommand());
|
||||
AZ_Assert(data, "FileRequest queued on StorageDrive to be read didn't contain read data.");
|
||||
|
||||
SystemFile* file = nullptr;
|
||||
@@ -342,7 +342,7 @@ namespace AZ::IO
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_getFileExistsTimeAverage);
|
||||
|
||||
auto& fileExists = AZStd::get<FileRequest::FileExistsCheckData>(request->GetCommand());
|
||||
auto& fileExists = AZStd::get<Requests::FileExistsCheckData>(request->GetCommand());
|
||||
size_t cacheIndex = FindFileInCache(fileExists.m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
{
|
||||
@@ -360,7 +360,7 @@ namespace AZ::IO
|
||||
AZ_PROFILE_FUNCTION(AzCore);
|
||||
TIMED_AVERAGE_WINDOW_SCOPE(m_getFileMetaDataTimeAverage);
|
||||
|
||||
auto& command = AZStd::get<FileRequest::FileMetaDataRetrievalData>(request->GetCommand());
|
||||
auto& command = AZStd::get<Requests::FileMetaDataRetrievalData>(request->GetCommand());
|
||||
// If the file is already open, use the file handle which usually is cheaper than asking for the file by name.
|
||||
size_t cacheIndex = FindFileInCache(command.m_path);
|
||||
if (cacheIndex != s_fileNotFound)
|
||||
@@ -446,11 +446,11 @@ namespace AZ::IO
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDrive::Report(const FileRequest::ReportData& data) const
|
||||
void StorageDrive::Report(const Requests::ReportData& data) const
|
||||
{
|
||||
switch (data.m_reportType)
|
||||
{
|
||||
case FileRequest::ReportData::ReportType::FileLocks:
|
||||
case Requests::ReportType::FileLocks:
|
||||
for (u32 i = 0; i < m_fileHandles.size(); ++i)
|
||||
{
|
||||
if (m_fileHandles[i] != nullptr)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/IO/Streamer/RequestPath.h>
|
||||
#include <AzCore/IO/Streamer/Statistics.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
#include <AzCore/IO/Streamer/StreamStackEntry.h>
|
||||
@@ -16,6 +17,11 @@
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/chrono/clocks.h>
|
||||
|
||||
namespace AZ::IO::Requests
|
||||
{
|
||||
struct ReportData;
|
||||
}
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
struct StorageDriveConfig final :
|
||||
@@ -72,7 +78,7 @@ namespace AZ::IO
|
||||
void EstimateCompletionTimeForRequest(FileRequest* request, AZStd::chrono::system_clock::time_point& startTime,
|
||||
const RequestPath*& activeFile, u64& activeOffset) const;
|
||||
|
||||
void Report(const FileRequest::ReportData& data) const;
|
||||
void Report(const Requests::ReportData& data) const;
|
||||
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_fileOpenCloseTimeAverage;
|
||||
TimedAverageWindow<s_statisticsWindowSize> m_getFileExistsTimeAverage;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/IO/CompressionBus.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/IO/Streamer/Streamer.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
#include <AzCore/IO/Streamer/StreamStackEntry.h>
|
||||
@@ -210,7 +211,7 @@ namespace AZ::IO
|
||||
IStreamerTypes::ClaimMemory claimMemory) const
|
||||
{
|
||||
AZ_Assert(request.m_request, "The request handle provided to Streamer::GetReadRequestResult is invalid.");
|
||||
auto readRequest = AZStd::get_if<FileRequest::ReadRequestData>(&request.m_request->GetCommand());
|
||||
auto readRequest = AZStd::get_if<Requests::ReadRequestData>(&request.m_request->GetCommand());
|
||||
if (readRequest != nullptr)
|
||||
{
|
||||
buffer = readRequest->m_output;
|
||||
@@ -281,14 +282,14 @@ namespace AZ::IO
|
||||
}
|
||||
}
|
||||
|
||||
FileRequestPtr Streamer::Report(FileRequest::ReportData::ReportType reportType)
|
||||
FileRequestPtr Streamer::Report(Requests::ReportType reportType)
|
||||
{
|
||||
FileRequestPtr result = CreateRequest();
|
||||
Report(result, reportType);
|
||||
return result;
|
||||
}
|
||||
|
||||
FileRequestPtr& Streamer::Report(FileRequestPtr& request, FileRequest::ReportData::ReportType reportType)
|
||||
FileRequestPtr& Streamer::Report(FileRequestPtr& request, Requests::ReportType reportType)
|
||||
{
|
||||
request->m_request.CreateReport(reportType);
|
||||
return request;
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace AZStd
|
||||
struct thread_desc;
|
||||
}
|
||||
|
||||
namespace AZ::IO::Requests
|
||||
{
|
||||
enum class ReportType : int8_t;
|
||||
}
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
@@ -185,9 +189,9 @@ namespace AZ::IO
|
||||
void RecordStatistics();
|
||||
|
||||
//! Tells AZ::IO::Streamer the report the information for the report to the output.
|
||||
FileRequestPtr Report(FileRequest::ReportData::ReportType reportType);
|
||||
FileRequestPtr Report(Requests::ReportType reportType);
|
||||
//! Tells AZ::IO::Streamer the report the information for the report to the output.
|
||||
FileRequestPtr& Report(FileRequestPtr& request, FileRequest::ReportData::ReportType reportType);
|
||||
FileRequestPtr& Report(FileRequestPtr& request, Requests::ReportType reportType);
|
||||
|
||||
|
||||
Streamer(const AZStd::thread_desc& threadDesc, AZStd::unique_ptr<Scheduler> streamStack);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/IO/Streamer/BlockCache.h>
|
||||
#include <AzCore/IO/Streamer/DedicatedCache.h>
|
||||
#include <AzCore/IO/Streamer/FullFileDecompressor.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/IO/Streamer/Scheduler.h>
|
||||
#include <AzCore/IO/Streamer/StreamerComponent.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
@@ -207,7 +208,7 @@ namespace AZ
|
||||
{
|
||||
if (m_streamer)
|
||||
{
|
||||
m_streamer->QueueRequest(m_streamer->Report(AZ::IO::FileRequest::ReportData::ReportType::FileLocks));
|
||||
m_streamer->QueueRequest(m_streamer->Report(AZ::IO::Requests::ReportType::FileLocks));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace AZ
|
||||
static constexpr char LatePredictionName[] = "Early completions";
|
||||
static constexpr char MissedDeadlinesName[] = "Missed deadlines";
|
||||
#endif // AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
|
||||
|
||||
StreamerContext::StreamerContext() = default;
|
||||
|
||||
StreamerContext::~StreamerContext()
|
||||
{
|
||||
for (FileRequest* entry : m_internalRecycleBin)
|
||||
@@ -204,7 +207,7 @@ namespace AZ
|
||||
m_latePredictionsPercentageStat.GetMostRecentSample());
|
||||
}
|
||||
}
|
||||
auto readRequest = AZStd::get_if<FileRequest::ReadRequestData>(&top->GetCommand());
|
||||
auto readRequest = AZStd::get_if<Requests::ReadRequestData>(&top->GetCommand());
|
||||
if (readRequest != nullptr)
|
||||
{
|
||||
m_missedDeadlinePercentageStat.PushSample(now < readRequest->m_deadline ? 0.0 : 1.0);
|
||||
@@ -224,7 +227,7 @@ namespace AZ
|
||||
top->m_onCompletion(*top);
|
||||
AZ_PROFILE_INTERVAL_END(AzCore, top);
|
||||
}
|
||||
|
||||
|
||||
if (parent)
|
||||
{
|
||||
AZ_Assert(parent->m_dependencies > 0,
|
||||
|
||||
@@ -8,22 +8,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/IO/Streamer/FileRequest.h>
|
||||
#include <AzCore/IO/Streamer/Statistics.h>
|
||||
#include <AzCore/IO/Streamer/StreamerConfiguration.h>
|
||||
#include <AzCore/IO/Streamer/StreamerContext_Platform.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/Statistics/RunningStatistic.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
class FileRequest;
|
||||
class ExternalFileRequest;
|
||||
|
||||
using FileRequestPtr = AZStd::intrusive_ptr<ExternalFileRequest>;
|
||||
|
||||
class StreamerContext
|
||||
{
|
||||
public:
|
||||
using PreparedQueue = AZStd::deque<FileRequest*>;
|
||||
|
||||
StreamerContext();
|
||||
~StreamerContext();
|
||||
|
||||
//! Gets a new file request, either by creating a new instance or
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace AZ
|
||||
|
||||
void Matrix3x4SetRowGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc)
|
||||
{
|
||||
bool rowIsSet = false;
|
||||
[[maybe_unused]] bool rowIsSet = false;
|
||||
if (dc.GetNumArguments() >= 5)
|
||||
{
|
||||
if (dc.IsNumber(0))
|
||||
@@ -88,7 +88,7 @@ namespace AZ
|
||||
|
||||
void Matrix3x4SetColumnGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc)
|
||||
{
|
||||
bool columnIsSet = false;
|
||||
[[maybe_unused]] bool columnIsSet = false;
|
||||
if (dc.GetNumArguments() >= 4)
|
||||
{
|
||||
if (dc.IsNumber(0))
|
||||
@@ -133,7 +133,7 @@ namespace AZ
|
||||
|
||||
void Matrix3x4SetTranslationGeneric(Matrix3x4* thisPtr, ScriptDataContext& dc)
|
||||
{
|
||||
bool translationIsSet = false;
|
||||
[[maybe_unused]] bool translationIsSet = false;
|
||||
|
||||
if (dc.GetNumArguments() == 3 &&
|
||||
dc.IsNumber(0) &&
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace AZ
|
||||
|
||||
// check open brace
|
||||
char c = *current++;
|
||||
bool has_open_brace = false;
|
||||
[[maybe_unused]] bool has_open_brace = false;
|
||||
if (c == '{')
|
||||
{
|
||||
c = *current++;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace AZ
|
||||
|
||||
NameDictionary::~NameDictionary()
|
||||
{
|
||||
bool leaksDetected = false;
|
||||
[[maybe_unused]] bool leaksDetected = false;
|
||||
|
||||
for (const auto& keyValue : m_dictionary)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}");
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
enum class ObjectStreamWriteOverrideResponse;
|
||||
|
||||
namespace VariantSerializationInternal
|
||||
{
|
||||
template <class ValueType>
|
||||
@@ -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;
|
||||
|
||||
@@ -116,6 +116,8 @@ set(FILES
|
||||
Debug/TraceReflection.h
|
||||
DOM/DomBackend.cpp
|
||||
DOM/DomBackend.h
|
||||
DOM/DomPath.cpp
|
||||
DOM/DomPath.h
|
||||
DOM/DomUtils.cpp
|
||||
DOM/DomUtils.h
|
||||
DOM/DomValue.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,9 +7,37 @@
|
||||
*/
|
||||
#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
|
||||
{
|
||||
@@ -33,97 +61,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<N == 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<N == dynamic_extent || N == Extent>>
|
||||
constexpr span(array<U, N>& data) noexcept;
|
||||
template <class U, size_t N, class = enable_if_t<N == 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>
|
||||
@@ -613,8 +614,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 +963,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 +1009,15 @@ namespace AZStd
|
||||
|
||||
} // namespace AZStd
|
||||
|
||||
namespace AZStd::ranges
|
||||
{
|
||||
template <class Element, class Traits>
|
||||
inline constexpr bool enable_borrowed_range<basic_string_view<Element, Traits>> = true;
|
||||
|
||||
template <class Element, class Traits>
|
||||
inline constexpr bool enable_view<basic_string_view<Element, Traits>> = true;
|
||||
}
|
||||
|
||||
//! Use this macro to simplify safe printing of a string_view which may not be null-terminated.
|
||||
//! Example: AZStd::string::format("Safely formatted: %.*s", AZ_STRING_ARG(myString));
|
||||
#define AZ_STRING_ARG(str) aznumeric_cast<int>(str.size()), str.data()
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/typetraits/config.h>
|
||||
#include <AzCore/std/typetraits/common_type.h>
|
||||
#include <AzCore/std/typetraits/conditional.h>
|
||||
#include <AzCore/std/typetraits/is_const.h>
|
||||
#include <AzCore/std/typetraits/is_convertible.h>
|
||||
#include <AzCore/std/typetraits/is_lvalue_reference.h>
|
||||
#include <AzCore/std/typetraits/is_rvalue_reference.h>
|
||||
#include <AzCore/std/typetraits/is_same.h>
|
||||
#include <AzCore/std/typetraits/is_volatile.h>
|
||||
#include <AzCore/std/typetraits/remove_reference.h>
|
||||
#include <AzCore/std/typetraits/remove_cvref.h>
|
||||
#include <AzCore/std/typetraits/void_t.h>
|
||||
#include <AzCore/std/utility/declval.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
template <class T, class U, template<class> class TQual, template<class> class UQual>
|
||||
struct basic_common_reference
|
||||
{};
|
||||
}
|
||||
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
// const volatile and reference qualifier copy templates
|
||||
template <class T, class QualType>
|
||||
struct copy_cv_qual
|
||||
{
|
||||
using type = conditional_t<is_const_v<T>, conditional_t<is_volatile_v<T>, const volatile QualType, const QualType>,
|
||||
conditional_t<is_volatile_v<T>, volatile QualType, QualType>>;
|
||||
};
|
||||
|
||||
template <class T,class QualType>
|
||||
using copy_cv_qual_t = typename copy_cv_qual<T, QualType>::type;
|
||||
|
||||
static_assert(is_same_v<copy_cv_qual_t<int, float>, float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const int, float>, const float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<volatile int, float>, volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const volatile int, float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<int, const float>, const float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const int, const float>, const float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<volatile int, const float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const volatile int, const float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<int, volatile float>, volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const int, volatile float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<volatile int, volatile float>, volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const volatile int, volatile float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<int, const volatile float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const int, const volatile float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<volatile int, const volatile float>, const volatile float>);
|
||||
static_assert(is_same_v<copy_cv_qual_t<const volatile int, const volatile float>, const volatile float>);
|
||||
|
||||
template <class T, class QualType>
|
||||
struct copy_reference_qual
|
||||
{
|
||||
using type = conditional_t<is_lvalue_reference_v<T>, QualType&,
|
||||
conditional_t<is_rvalue_reference_v<T>, QualType&&, QualType>>;
|
||||
};
|
||||
|
||||
template <class T, class QualType>
|
||||
using copy_reference_qual_t = typename copy_reference_qual<T, QualType>::type;
|
||||
|
||||
static_assert(is_same_v<copy_reference_qual_t<int, float>, float>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int&, float>, float&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int&&, float>, float&&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int, float&>, float&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int&, float&>, float&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int&&, float&>, float&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int, float&&>, float&&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int&, float&&>, float&>);
|
||||
static_assert(is_same_v<copy_reference_qual_t<int&&, float&&>, float&&>);
|
||||
|
||||
template <class T, class QualType>
|
||||
using copy_cvref_qual_t = copy_cv_qual_t<copy_reference_qual_t<T, QualType>, QualType>;
|
||||
|
||||
template <class T>
|
||||
struct copy_qualifiers_from_t
|
||||
{
|
||||
template <class X>
|
||||
using templ = copy_cvref_qual_t<T, X>;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
using cond_res = decltype(false ? declval<copy_cv_qual_t<remove_reference_t<T>, remove_reference_t<U>>&>()
|
||||
: declval<copy_cv_qual_t<remove_reference_t<U>, remove_reference_t<T>>&>());
|
||||
|
||||
// common reference helper templates begin
|
||||
template <class T, class U, typename = void>
|
||||
struct common_reference_base_reference_test;
|
||||
|
||||
// COMMON_REF is defined within the C++ standard at https://eel.is/c++draft/meta.trans.other#3.5
|
||||
template <class T, class U>
|
||||
struct common_reference_base_reference_test<T, U,
|
||||
enable_if_t<is_lvalue_reference_v<T>&& is_lvalue_reference_v<U>,
|
||||
void_t<cond_res<T,U>> >>
|
||||
{
|
||||
// Uses the ternary operator for determining the common type
|
||||
using type = cond_res<T, U>;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T>&& is_rvalue_reference_v<U>>>
|
||||
{
|
||||
using C = remove_reference_t<typename common_reference_base_reference_test<remove_reference_t<T>&, remove_reference_t<U>&>::type>;
|
||||
using type = AZStd::enable_if_t<is_convertible_v<T, C>&& is_convertible_v<U, C>, C>;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
struct common_reference_base_reference_test<T, U, enable_if_t<is_rvalue_reference_v<T>&& is_lvalue_reference_v<U>>>
|
||||
{
|
||||
// Turn rvalue references to const lvalue references
|
||||
using D = typename common_reference_base_reference_test<const remove_reference_t<T>&, remove_reference_t<U>&>::type;
|
||||
using type = AZStd::enable_if_t<is_convertible_v<T, D>, D>;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
struct common_reference_base_reference_test<T, U, enable_if_t<is_lvalue_reference_v<T>&& is_rvalue_reference_v<U>>>
|
||||
{
|
||||
// Swap the parameters to call the 3rd specialization for common_reference_base_reference_test
|
||||
using type = typename common_reference_base_reference_test<U, T>::type;
|
||||
};
|
||||
|
||||
template <class T, class U, typename = void>
|
||||
constexpr bool has_reference_test = false;
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool has_reference_test<T, U, void_t<typename common_reference_base_reference_test<T, U>::type>> = true;
|
||||
|
||||
template <class T, class U, typename = void>
|
||||
struct basic_common_reference_test;
|
||||
|
||||
template <class T, class U>
|
||||
struct basic_common_reference_test<T, U, void_t<typename basic_common_reference<remove_cvref_t<T>, remove_cvref_t<U>,
|
||||
copy_qualifiers_from_t<T>::template templ, copy_qualifiers_from_t<U>::template templ>::type>>
|
||||
{
|
||||
using type = typename basic_common_reference<remove_cvref_t<T>, remove_cvref_t<U>,
|
||||
copy_qualifiers_from_t<T>::template templ, copy_qualifiers_from_t<U>::template templ>::type;
|
||||
};
|
||||
|
||||
template <class T, class U, typename = void>
|
||||
constexpr bool has_basic_common_reference_test = false;
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool has_basic_common_reference_test<T, U,
|
||||
void_t<typename basic_common_reference_test<T, U>::type>> = true;
|
||||
|
||||
template <class T, class U, typename = void>
|
||||
constexpr bool has_condition_result_test = false;
|
||||
|
||||
template <class T, class U>
|
||||
constexpr bool has_condition_result_test<T, U, void_t<decltype(false ? declval<T>() : declval<U>())>> = true;
|
||||
|
||||
template <class T, class U, typename = void>
|
||||
struct common_reference_base_test
|
||||
{};
|
||||
|
||||
template <class T, class U>
|
||||
struct common_reference_base_test<T, U, enable_if_t<has_reference_test<T, U>>>
|
||||
: common_reference_base_reference_test<T, U>
|
||||
{};
|
||||
template <class T, class U>
|
||||
struct common_reference_base_test<T, U, enable_if_t<!has_reference_test<T, U>
|
||||
&& has_basic_common_reference_test<T, U>>>
|
||||
: basic_common_reference_test<T, U>
|
||||
{};
|
||||
template <class T, class U>
|
||||
struct common_reference_base_test<T, U, enable_if_t<!has_reference_test<T, U>
|
||||
&& !has_basic_common_reference_test<T, U> && has_condition_result_test<T,U>>>
|
||||
{
|
||||
using type = decltype(false ? declval<T>() : declval<U>());
|
||||
};
|
||||
template <class T, class U>
|
||||
struct common_reference_base_test<T, U, enable_if_t<!has_reference_test<T, U>
|
||||
&& !has_basic_common_reference_test<T, U> && !has_condition_result_test<T, U>>>
|
||||
: common_type<T, U>
|
||||
{};
|
||||
|
||||
template <class... T>
|
||||
struct common_reference_base
|
||||
{};
|
||||
|
||||
template <class T>
|
||||
struct common_reference_base<T>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
template <class T, class U>
|
||||
struct common_reference_base<T, U>
|
||||
: common_reference_base_test<T, U>
|
||||
{};
|
||||
|
||||
template <class T, class U, class V, class... Rs>
|
||||
struct common_reference_base<T, U, V, Rs...>
|
||||
: common_reference_base<typename common_reference_base<T, U>::type, V, Rs...>
|
||||
{};
|
||||
}
|
||||
namespace AZStd
|
||||
{
|
||||
template <class... T>
|
||||
struct common_reference
|
||||
: Internal::common_reference_base<T...>
|
||||
{};
|
||||
|
||||
template <class... T>
|
||||
using common_reference_t = typename common_reference<T...>::type;
|
||||
|
||||
// models the common reference concept
|
||||
namespace Internal
|
||||
{
|
||||
template<class T, class U, typename = void>
|
||||
constexpr bool common_reference_with_impl = false;
|
||||
template<class T, class U>
|
||||
constexpr bool common_reference_with_impl<T, U, enable_if_t<
|
||||
same_as<common_reference_t<T, U>, common_reference_t<U, T>>
|
||||
&& convertible_to<T, common_reference_t<T, U>>
|
||||
&& convertible_to<U, common_reference_t<T, U>>
|
||||
>> = true;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
/*concept*/ constexpr bool common_reference_with = Internal::common_reference_with_impl<T, U>;
|
||||
}
|
||||
@@ -8,12 +8,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/typetraits/conditional.h>
|
||||
#include <AzCore/std/typetraits/intrinsics.h>
|
||||
#include <AzCore/std/typetraits/void_t.h>
|
||||
#include <AzCore/std/utility/declval.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
using std::is_convertible;
|
||||
using std::is_convertible_v;
|
||||
|
||||
// models the C++20 convertible_to concept
|
||||
namespace Internal
|
||||
{
|
||||
template<typename From, typename To, typename = void>
|
||||
constexpr bool convertible_to_impl = false;
|
||||
template<typename From, typename To>
|
||||
constexpr bool convertible_to_impl<From, To, enable_if_t<
|
||||
is_convertible_v<From, To>, void_t<decltype(static_cast<To>(declval<From>()))>>> = true;
|
||||
}
|
||||
template<typename From, typename To>
|
||||
constexpr bool is_convertible_v = std::is_convertible_v<From, To>;
|
||||
/*concept*/ constexpr bool convertible_to = Internal::convertible_to_impl<From, To>;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,4 +21,7 @@ namespace AZStd
|
||||
constexpr bool is_trivially_destructible_v = std::is_trivially_destructible<T>::value;
|
||||
template<class T>
|
||||
constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible<T>::value;
|
||||
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool destructible = is_nothrow_destructible_v<T>;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,7 @@ namespace AZStd
|
||||
{
|
||||
using std::is_floating_point;
|
||||
using std::is_floating_point_v;
|
||||
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool floating_point = is_floating_point_v<T>;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,7 @@ namespace AZStd
|
||||
{
|
||||
using std::is_integral;
|
||||
using std::is_integral_v;
|
||||
|
||||
template<class T>
|
||||
/*concept*/ constexpr bool integral = is_integral_v<T>;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,8 @@ namespace AZStd
|
||||
{
|
||||
using std::is_same;
|
||||
using std::is_same_v;
|
||||
|
||||
// models the same_as concept
|
||||
template <class T, class U>
|
||||
/*concept*/ constexpr bool same_as = is_same_v<T, U>;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <AzCore/std/typetraits/add_volatile.h>
|
||||
#include <AzCore/std/typetraits/alignment_of.h>
|
||||
#include <AzCore/std/typetraits/aligned_storage.h>
|
||||
#include <AzCore/std/typetraits/common_reference.h>
|
||||
#include <AzCore/std/typetraits/common_type.h>
|
||||
#include <AzCore/std/typetraits/conditional.h>
|
||||
#include <AzCore/std/typetraits/conjunction.h>
|
||||
#include <AzCore/std/typetraits/decay.h>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 <utility>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
using std::declval;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// rvalue
|
||||
// rvalue move
|
||||
template<class T>
|
||||
constexpr AZStd::remove_reference_t<T>&& move(T&& t)
|
||||
{
|
||||
return static_cast<AZStd::remove_reference_t<T>&&>(t);
|
||||
}
|
||||
}
|
||||
@@ -22,22 +22,15 @@
|
||||
#include <AzCore/std/typetraits/is_convertible.h>
|
||||
#include <AzCore/std/typetraits/is_lvalue_reference.h>
|
||||
#include <AzCore/std/typetraits/void_t.h>
|
||||
#include <AzCore/std/utility/declval.h>
|
||||
#include <AzCore/std/utility/move.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// rvalue
|
||||
// rvalue move
|
||||
template<class T>
|
||||
constexpr AZStd::remove_reference_t<T>&& move(T && t)
|
||||
{
|
||||
return static_cast<AZStd::remove_reference_t<T>&&>(t);
|
||||
}
|
||||
|
||||
using std::forward;
|
||||
using std::declval;
|
||||
using std::exchange;
|
||||
|
||||
template <class T>
|
||||
|
||||
Reference in New Issue
Block a user