Update amdgpu-windows-interop with latest changes (#1718)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
280cda3196
commit
321e497048
@@ -1,368 +1,368 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palAssert.h
|
||||
* @brief PAL utility collection assert macros.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palDbgPrint.h"
|
||||
#include "palUtil.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <intrin.h>
|
||||
/// OS-independent macro to force a break into the debugger.
|
||||
#define PAL_DEBUG_BREAK() [[unlikely]] __debugbreak()
|
||||
#else
|
||||
#include <signal.h>
|
||||
/// OS-independent macro to force a break into the debugger.
|
||||
#define PAL_DEBUG_BREAK() [[unlikely]] raise(SIGTRAP);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
/// Macro to direct static code analysis to assume the specified expression will always be true.
|
||||
/// Purpose is to suppress warnings from MSVC's /analysis setting.
|
||||
/// Only pertains to static code analysis. Does not impact compile optimization. Not the same as C++23's [[assume]].
|
||||
# define PAL_ANALYSIS_ASSUME(_expr) __analysis_assume(_expr)
|
||||
#else
|
||||
/// This macro is only useful on MSVC builds. It has no meaning for other builds.
|
||||
# define PAL_ANALYSIS_ASSUME(_expr) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// A helper function to check the size-in-bits of a 'reserved' member in a bitfield.
|
||||
/// This is intended for use with static_asserts to ensure things don't go out-of-sync.
|
||||
///
|
||||
/// @param [in] expectedTotalBitWidth Number of bits expected in the whole type
|
||||
/// @param [in] expectedReservedBits Number of bits in the 'reserved' field
|
||||
///
|
||||
/// @return true if the bit lengths of the type T match the values in the args.
|
||||
/// true if the compiler lacks support to do this at compile time.
|
||||
///
|
||||
/// @note This may not work properly with old compilers, but this is meant for linting anyhow.
|
||||
template <typename T>
|
||||
constexpr bool CheckReservedBits(
|
||||
uint32 expectedTotalBitWidth,
|
||||
uint32 expectedReservedBits)
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
// Fail if the whole size is different
|
||||
if (sizeof(T) * 8 == expectedTotalBitWidth)
|
||||
{
|
||||
// Get the width of the reserved field by detecting when it stops filling bits
|
||||
T sample = {};
|
||||
uint64 mask = 0;
|
||||
uint32 reservedBits = 0;
|
||||
do
|
||||
{
|
||||
sample = {};
|
||||
mask = (mask << 1) | 1;
|
||||
reservedBits++;
|
||||
sample.reserved = mask;
|
||||
} while ((sample.reserved == mask) && (reservedBits < sizeof(T) * 8));
|
||||
// when the loop terminates, it's one past the size of the field.
|
||||
match = (reservedBits - 1) == expectedReservedBits;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/// A helper function to check that a series of static numeric values are sequential.
|
||||
/// This is intended for use with static_asserts to ensure things don't go out-of-sync.
|
||||
///
|
||||
/// @param [in] args Array of numeric values to check
|
||||
/// @param [in] interval Expected interval between each (default 1, 4 is also common for field offsets)
|
||||
///
|
||||
/// @return true if all the values are sequential
|
||||
/// true if the compiler lacks support to do this at compile time.
|
||||
///
|
||||
/// @note This may not work properly with old compilers, but this is meant for linting anyhow.
|
||||
template <typename T, size_t N>
|
||||
constexpr bool CheckSequential(
|
||||
const T (&args)[N],
|
||||
T interval = 1)
|
||||
{
|
||||
bool isSequential = true;
|
||||
for (int i = 0; i < (N - 1); i++)
|
||||
{
|
||||
if ((args[i] + interval) != args[i + 1])
|
||||
{
|
||||
isSequential = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isSequential;
|
||||
}
|
||||
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS || PAL_ENABLE_LOGGING)
|
||||
|
||||
/// Specifies how severe an triggered assert (or alert) is.
|
||||
///
|
||||
/// Both asserts and alerts can print out a debug string and break into the debugger. Asserts are to be used to verify
|
||||
/// the known, assumed state of the program at any time. Alerts are to be used to notify the developer of a _possible_,
|
||||
/// but unexpected condition such as memory allocation failure, an OS call failure, or an application behavior that is
|
||||
/// known to be slow.
|
||||
enum AssertCategory : uint32
|
||||
{
|
||||
AssertCatAssert = 0,
|
||||
AssertCatAlert,
|
||||
AssertCatCount
|
||||
};
|
||||
|
||||
/// Enables/disables the specified assert category.
|
||||
///
|
||||
/// Probably controlled by a setting and set during initialization.
|
||||
///
|
||||
/// @param [in] category Assert category to enable/disable (asserts or alerts).
|
||||
/// @param [in] enable True to enable the specified assert category, false to disable it.
|
||||
extern void EnableAssertMode(
|
||||
AssertCategory category,
|
||||
bool enable);
|
||||
|
||||
/// Returns true if the specified assert category is enabled and false otherwise.
|
||||
///
|
||||
/// @param [in] category Assert category to check
|
||||
extern bool IsAssertCategoryEnabled(
|
||||
AssertCategory category);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Util
|
||||
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS || PAL_ENABLE_LOGGING)
|
||||
/// Prints an error message with the specified reason via the debug print system. A debug break will also be triggered
|
||||
/// if they're currently enabled for asserts.
|
||||
///
|
||||
/// @note This version of assert inlines an 'int 3' every time it is used so that each occurrence can be zapped
|
||||
/// independently. This macro cannot be used in assignment operations.
|
||||
#define PAL_TRIGGER_ASSERT(_pFormat, ...) [[unlikely]] \
|
||||
do { \
|
||||
PAL_DPERROR(_pFormat, ##__VA_ARGS__); \
|
||||
if (::Util::IsAssertCategoryEnabled(::Util::AssertCatAssert)) \
|
||||
{ \
|
||||
PAL_DEBUG_BREAK(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
/// If the expression evaluates to false, then it calls the PAL_TRIGGER_ASSERT macro with an error message with the
|
||||
/// specified reason.
|
||||
///
|
||||
/// @note This assert should not be used in constant evaluated contexts (e.g., constexpr functions).
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr_eval == false) [[unlikely]]
|
||||
// {
|
||||
// PAL_TRIGGER_ASSERT(...);
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So:
|
||||
// 1. we ignore PAL coding standards and don't use a compound statement;
|
||||
// 2. we don't use [[unlikely]] as the expansion of PAL_TRIGGER_ASSERT already has one.
|
||||
#define PAL_ASSERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
const bool _expr_eval = static_cast<bool>(_expr); \
|
||||
if (_expr_eval == false) \
|
||||
PAL_TRIGGER_ASSERT("Assertion failed: %s | Reason: " _pReasonFmt, #_expr, ##__VA_ARGS__); \
|
||||
PAL_ANALYSIS_ASSUME(_expr_eval); \
|
||||
} while (false)
|
||||
|
||||
#if !defined(__clang__) && !defined(_MSC_VER) && (__GNUC__ < 6)
|
||||
|
||||
// Function to circumvent gcc 5.x inability to use lambdas in unevaluated constant expression contexts.
|
||||
constexpr void PalTriggerAssertImpl(
|
||||
const char* pFormat,
|
||||
const char* pExpr,
|
||||
const char* pFile,
|
||||
int line,
|
||||
const char* pFunc)
|
||||
{
|
||||
// pExpr is always not nullptr, as it's supposed to be a preprocessor string, but it does convince gcc
|
||||
// to compile PalTriggerAssertImpl() as potentially constexpr
|
||||
pExpr != nullptr ?
|
||||
[&]
|
||||
{
|
||||
Util::DbgPrintf(
|
||||
Util::DbgPrintCatErrorMsg,
|
||||
Util::DbgPrintStyleDefault,
|
||||
pFormat,
|
||||
pExpr,
|
||||
pFile,
|
||||
line,
|
||||
pFunc);
|
||||
if (Util::IsAssertCategoryEnabled(Util::AssertCatAssert))
|
||||
{
|
||||
PAL_DEBUG_BREAK();
|
||||
}
|
||||
return 0;
|
||||
}()
|
||||
: 0;
|
||||
}
|
||||
|
||||
// gcc 5.4 implementation of PAL_CONSTEXPR_ASSERT_MSG that ignores the additional reason for the assertion
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr_eval == false) [[unlikely]]
|
||||
// {
|
||||
// PalTriggerAssertImpl(...);
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So we ignore PAL coding standards and don't use a compound statement.
|
||||
#define PAL_CONSTEXPR_ASSERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
const bool _expr_eval = static_cast<bool>(_expr); \
|
||||
if (_expr_eval == false) [[unlikely]] \
|
||||
PalTriggerAssertImpl("Assertion failed: %s (%s:%d:%s)", #_expr, __FILE__, __LINE__, __func__); \
|
||||
PAL_ANALYSIS_ASSUME(_expr_eval); \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
||||
/// If the expression evaluates to false, then it calls the PAL_TRIGGER_ASSERT macro with an error message with the
|
||||
/// specified reason.
|
||||
///
|
||||
/// @note This assert should be used in constant evaluated contexts (e.g., constexpr functions).
|
||||
/// @note This assert uses an immediately-invoked function expression in the form of an internal lambda to signal a
|
||||
/// failed assert. Since PAL_TRIGGER_ASSERT is not constexpr, an _expr that evaluates to false will fail to
|
||||
/// compile the function operator of the lambda.
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr_eval == false) [[unlikely]]
|
||||
// {
|
||||
// [&] { PAL_TRIGGER_ASSERT(...); }();
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So we ignore PAL coding standards and don't use a compound statement.
|
||||
#define PAL_CONSTEXPR_ASSERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
const bool _expr_eval = static_cast<bool>(_expr); \
|
||||
if (_expr_eval == false) [[unlikely]] \
|
||||
[&] { PAL_TRIGGER_ASSERT("Assertion failed: %s | Reason: " _pReasonFmt, #_expr, ##__VA_ARGS__); }(); \
|
||||
PAL_ANALYSIS_ASSUME(_expr_eval); \
|
||||
} while (false)
|
||||
|
||||
#endif
|
||||
|
||||
/// Calls the PAL_ASSERT_MSG macro with a generic reason string
|
||||
#define PAL_ASSERT(_expr) PAL_ASSERT_MSG(_expr, "%s", "Unknown")
|
||||
|
||||
/// Calls the PAL_CONSTEXPR_ASSERT_MSG macro with a generic reason string
|
||||
#define PAL_CONSTEXPR_ASSERT(_expr) PAL_CONSTEXPR_ASSERT_MSG(_expr, "%s", "Unknown")
|
||||
|
||||
#if DEBUG
|
||||
/// Debug build only PAL assert, the typical usage is when make an assertion on a debug-only variables.
|
||||
/// The only difference than PAL assert is it's empty in release mode.
|
||||
#define PAL_DEBUG_BUILD_ONLY_ASSERT(_expr) \
|
||||
do { \
|
||||
PAL_ASSERT(_expr); \
|
||||
} while (false)
|
||||
#else
|
||||
#define PAL_DEBUG_BUILD_ONLY_ASSERT(_expr) ((void)0)
|
||||
#endif
|
||||
|
||||
/// If the expression evaluates to true, then a warning message with the specified reason will be printed via the
|
||||
/// debug print system. A debug break will also be triggered if they're currently enabled for alerts.
|
||||
///
|
||||
/// @note This is the opposite polarity of asserts. The assert macro _asserts_ that the specified condition is true.
|
||||
/// While the alert macro _alerts_ the developer if the specified condition is true.
|
||||
///
|
||||
/// This macro should be used in places where an assert is inappropriate because an error condition is _possible_, but
|
||||
/// not typically expected. For example, asserting that an OS call succeeded should be avoided since there cannot be an
|
||||
/// assumption that it will succeed. Nonetheless, a developer may want to be alerted immediately and dropped into the
|
||||
/// debugger when such a failure occurs.
|
||||
#define PAL_TRIGGER_ALERT(_pFormat, ...) [[unlikely]] \
|
||||
do { \
|
||||
PAL_DPWARN(_pFormat, ##__VA_ARGS__); \
|
||||
if (::Util::IsAssertCategoryEnabled(::Util::AssertCatAlert)) \
|
||||
{ \
|
||||
PAL_DEBUG_BREAK(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr) [[unlikely]]
|
||||
// {
|
||||
// PAL_TRIGGER_ASSERT(...);
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So:
|
||||
// 1. we ignore PAL coding standards and don't use a compound statement;
|
||||
// 2. we don't use [[unlikely]] as the expansion of PAL_TRIGGER_ASSERT already has one.
|
||||
#define PAL_ALERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
if (_expr) \
|
||||
PAL_TRIGGER_ALERT("Alert triggered: %s | Reason: " _pReasonFmt, #_expr, ##__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
/// Calls the PAL_ALERT_MSG macro with a generic reason string
|
||||
#define PAL_ALERT(_expr) PAL_ALERT_MSG(_expr, "%s", "Unknown")
|
||||
|
||||
/// Convenience macro that asserts if something has never been tested.
|
||||
#define PAL_NOT_TESTED_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Code Not Tested! | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_NOT_TESTED() PAL_NOT_TESTED_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that asserts if something has not been implemented.
|
||||
#define PAL_NOT_IMPLEMENTED_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Not Implemented! | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_NOT_IMPLEMENTED() PAL_NOT_IMPLEMENTED_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that asserts if an area of code that shouldn't be executed is reached.
|
||||
#define PAL_NEVER_CALLED_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Code should never be called! | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_NEVER_CALLED() PAL_NEVER_CALLED_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that always asserts. Expect this to be used instead of PAL_ASSERT(false).
|
||||
#define PAL_ASSERT_ALWAYS_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Unconditional Assert | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_ASSERT_ALWAYS() PAL_ASSERT_ALWAYS_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that always alerts. Expect this to be used instead of PAL_ALERT(true).
|
||||
#define PAL_ALERT_ALWAYS_MSG(_pReasonFmt, ...) PAL_TRIGGER_ALERT("Unconditional Alert | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_ALERT_ALWAYS() PAL_ALERT_ALWAYS_MSG("%s", "Unknown")
|
||||
|
||||
#else
|
||||
|
||||
#define PAL_ASSERT(_expr) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_CONSTEXPR_ASSERT(_expr) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_ASSERT_MSG(_expr, ...) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_CONSTEXPR_ASSERT_MSG(_expr, ...) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_DEBUG_BUILD_ONLY_ASSERT(_expr) ((void)0)
|
||||
#define PAL_ALERT(_expr) ((void)0)
|
||||
#define PAL_ALERT_MSG(_expr, ...) ((void)0)
|
||||
#define PAL_NOT_TESTED() [[unlikely]] ((void)0)
|
||||
#define PAL_NOT_TESTED_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_NOT_IMPLEMENTED() [[unlikely]] ((void)0)
|
||||
#define PAL_NOT_IMPLEMENTED_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_NEVER_CALLED() [[unlikely]] ((void)0)
|
||||
#define PAL_NEVER_CALLED_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_ASSERT_ALWAYS() [[unlikely]] ((void)0)
|
||||
#define PAL_ASSERT_ALWAYS_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_ALERT_ALWAYS() [[unlikely]] ((void)0)
|
||||
#define PAL_ALERT_ALWAYS_MSG(...) [[unlikely]] ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palAssert.h
|
||||
* @brief PAL utility collection assert macros.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palDbgPrint.h"
|
||||
#include "palUtil.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <intrin.h>
|
||||
/// OS-independent macro to force a break into the debugger.
|
||||
#define PAL_DEBUG_BREAK() [[unlikely]] __debugbreak()
|
||||
#else
|
||||
#include <signal.h>
|
||||
/// OS-independent macro to force a break into the debugger.
|
||||
#define PAL_DEBUG_BREAK() [[unlikely]] raise(SIGTRAP);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
/// Macro to direct static code analysis to assume the specified expression will always be true.
|
||||
/// Purpose is to suppress warnings from MSVC's /analysis setting.
|
||||
/// Only pertains to static code analysis. Does not impact compile optimization. Not the same as C++23's [[assume]].
|
||||
# define PAL_ANALYSIS_ASSUME(_expr) __analysis_assume(_expr)
|
||||
#else
|
||||
/// This macro is only useful on MSVC builds. It has no meaning for other builds.
|
||||
# define PAL_ANALYSIS_ASSUME(_expr) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// A helper function to check the size-in-bits of a 'reserved' member in a bitfield.
|
||||
/// This is intended for use with static_asserts to ensure things don't go out-of-sync.
|
||||
///
|
||||
/// @param [in] expectedTotalBitWidth Number of bits expected in the whole type
|
||||
/// @param [in] expectedReservedBits Number of bits in the 'reserved' field
|
||||
///
|
||||
/// @return true if the bit lengths of the type T match the values in the args.
|
||||
/// true if the compiler lacks support to do this at compile time.
|
||||
///
|
||||
/// @note This may not work properly with old compilers, but this is meant for linting anyhow.
|
||||
template <typename T>
|
||||
constexpr bool CheckReservedBits(
|
||||
uint32 expectedTotalBitWidth,
|
||||
uint32 expectedReservedBits)
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
// Fail if the whole size is different
|
||||
if (sizeof(T) * 8 == expectedTotalBitWidth)
|
||||
{
|
||||
// Get the width of the reserved field by detecting when it stops filling bits
|
||||
T sample = {};
|
||||
uint64 mask = 0;
|
||||
uint32 reservedBits = 0;
|
||||
do
|
||||
{
|
||||
sample = {};
|
||||
mask = (mask << 1) | 1;
|
||||
reservedBits++;
|
||||
sample.reserved = mask;
|
||||
} while ((sample.reserved == mask) && (reservedBits < sizeof(T) * 8));
|
||||
// when the loop terminates, it's one past the size of the field.
|
||||
match = (reservedBits - 1) == expectedReservedBits;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/// A helper function to check that a series of static numeric values are sequential.
|
||||
/// This is intended for use with static_asserts to ensure things don't go out-of-sync.
|
||||
///
|
||||
/// @param [in] args Array of numeric values to check
|
||||
/// @param [in] interval Expected interval between each (default 1, 4 is also common for field offsets)
|
||||
///
|
||||
/// @return true if all the values are sequential
|
||||
/// true if the compiler lacks support to do this at compile time.
|
||||
///
|
||||
/// @note This may not work properly with old compilers, but this is meant for linting anyhow.
|
||||
template <typename T, size_t N>
|
||||
constexpr bool CheckSequential(
|
||||
const T (&args)[N],
|
||||
T interval = 1)
|
||||
{
|
||||
bool isSequential = true;
|
||||
for (int i = 0; i < (N - 1); i++)
|
||||
{
|
||||
if ((args[i] + interval) != args[i + 1])
|
||||
{
|
||||
isSequential = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isSequential;
|
||||
}
|
||||
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS || PAL_ENABLE_LOGGING)
|
||||
|
||||
/// Specifies how severe an triggered assert (or alert) is.
|
||||
///
|
||||
/// Both asserts and alerts can print out a debug string and break into the debugger. Asserts are to be used to verify
|
||||
/// the known, assumed state of the program at any time. Alerts are to be used to notify the developer of a _possible_,
|
||||
/// but unexpected condition such as memory allocation failure, an OS call failure, or an application behavior that is
|
||||
/// known to be slow.
|
||||
enum AssertCategory : uint32
|
||||
{
|
||||
AssertCatAssert = 0,
|
||||
AssertCatAlert,
|
||||
AssertCatCount
|
||||
};
|
||||
|
||||
/// Enables/disables the specified assert category.
|
||||
///
|
||||
/// Probably controlled by a setting and set during initialization.
|
||||
///
|
||||
/// @param [in] category Assert category to enable/disable (asserts or alerts).
|
||||
/// @param [in] enable True to enable the specified assert category, false to disable it.
|
||||
extern void EnableAssertMode(
|
||||
AssertCategory category,
|
||||
bool enable);
|
||||
|
||||
/// Returns true if the specified assert category is enabled and false otherwise.
|
||||
///
|
||||
/// @param [in] category Assert category to check
|
||||
extern bool IsAssertCategoryEnabled(
|
||||
AssertCategory category);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Util
|
||||
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS || PAL_ENABLE_LOGGING)
|
||||
/// Prints an error message with the specified reason via the debug print system. A debug break will also be triggered
|
||||
/// if they're currently enabled for asserts.
|
||||
///
|
||||
/// @note This version of assert inlines an 'int 3' every time it is used so that each occurrence can be zapped
|
||||
/// independently. This macro cannot be used in assignment operations.
|
||||
#define PAL_TRIGGER_ASSERT(_pFormat, ...) [[unlikely]] \
|
||||
do { \
|
||||
PAL_DPERROR(_pFormat, ##__VA_ARGS__); \
|
||||
if (::Util::IsAssertCategoryEnabled(::Util::AssertCatAssert)) \
|
||||
{ \
|
||||
PAL_DEBUG_BREAK(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
/// If the expression evaluates to false, then it calls the PAL_TRIGGER_ASSERT macro with an error message with the
|
||||
/// specified reason.
|
||||
///
|
||||
/// @note This assert should not be used in constant evaluated contexts (e.g., constexpr functions).
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr_eval == false) [[unlikely]]
|
||||
// {
|
||||
// PAL_TRIGGER_ASSERT(...);
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So:
|
||||
// 1. we ignore PAL coding standards and don't use a compound statement;
|
||||
// 2. we don't use [[unlikely]] as the expansion of PAL_TRIGGER_ASSERT already has one.
|
||||
#define PAL_ASSERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
const bool _expr_eval = static_cast<bool>(_expr); \
|
||||
if (_expr_eval == false) \
|
||||
PAL_TRIGGER_ASSERT("Assertion failed: %s | Reason: " _pReasonFmt, #_expr, ##__VA_ARGS__); \
|
||||
PAL_ANALYSIS_ASSUME(_expr_eval); \
|
||||
} while (false)
|
||||
|
||||
#if !defined(__clang__) && !defined(_MSC_VER) && (__GNUC__ < 6)
|
||||
|
||||
// Function to circumvent gcc 5.x inability to use lambdas in unevaluated constant expression contexts.
|
||||
constexpr void PalTriggerAssertImpl(
|
||||
const char* pFormat,
|
||||
const char* pExpr,
|
||||
const char* pFile,
|
||||
int line,
|
||||
const char* pFunc)
|
||||
{
|
||||
// pExpr is always not nullptr, as it's supposed to be a preprocessor string, but it does convince gcc
|
||||
// to compile PalTriggerAssertImpl() as potentially constexpr
|
||||
pExpr != nullptr ?
|
||||
[&]
|
||||
{
|
||||
Util::DbgPrintf(
|
||||
Util::DbgPrintCatErrorMsg,
|
||||
Util::DbgPrintStyleDefault,
|
||||
pFormat,
|
||||
pExpr,
|
||||
pFile,
|
||||
line,
|
||||
pFunc);
|
||||
if (Util::IsAssertCategoryEnabled(Util::AssertCatAssert))
|
||||
{
|
||||
PAL_DEBUG_BREAK();
|
||||
}
|
||||
return 0;
|
||||
}()
|
||||
: 0;
|
||||
}
|
||||
|
||||
// gcc 5.4 implementation of PAL_CONSTEXPR_ASSERT_MSG that ignores the additional reason for the assertion
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr_eval == false) [[unlikely]]
|
||||
// {
|
||||
// PalTriggerAssertImpl(...);
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So we ignore PAL coding standards and don't use a compound statement.
|
||||
#define PAL_CONSTEXPR_ASSERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
const bool _expr_eval = static_cast<bool>(_expr); \
|
||||
if (_expr_eval == false) [[unlikely]] \
|
||||
PalTriggerAssertImpl("Assertion failed: %s (%s:%d:%s)", #_expr, __FILE__, __LINE__, __func__); \
|
||||
PAL_ANALYSIS_ASSUME(_expr_eval); \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
||||
/// If the expression evaluates to false, then it calls the PAL_TRIGGER_ASSERT macro with an error message with the
|
||||
/// specified reason.
|
||||
///
|
||||
/// @note This assert should be used in constant evaluated contexts (e.g., constexpr functions).
|
||||
/// @note This assert uses an immediately-invoked function expression in the form of an internal lambda to signal a
|
||||
/// failed assert. Since PAL_TRIGGER_ASSERT is not constexpr, an _expr that evaluates to false will fail to
|
||||
/// compile the function operator of the lambda.
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr_eval == false) [[unlikely]]
|
||||
// {
|
||||
// [&] { PAL_TRIGGER_ASSERT(...); }();
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So we ignore PAL coding standards and don't use a compound statement.
|
||||
#define PAL_CONSTEXPR_ASSERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
const bool _expr_eval = static_cast<bool>(_expr); \
|
||||
if (_expr_eval == false) [[unlikely]] \
|
||||
[&] { PAL_TRIGGER_ASSERT("Assertion failed: %s | Reason: " _pReasonFmt, #_expr, ##__VA_ARGS__); }(); \
|
||||
PAL_ANALYSIS_ASSUME(_expr_eval); \
|
||||
} while (false)
|
||||
|
||||
#endif
|
||||
|
||||
/// Calls the PAL_ASSERT_MSG macro with a generic reason string
|
||||
#define PAL_ASSERT(_expr) PAL_ASSERT_MSG(_expr, "%s", "Unknown")
|
||||
|
||||
/// Calls the PAL_CONSTEXPR_ASSERT_MSG macro with a generic reason string
|
||||
#define PAL_CONSTEXPR_ASSERT(_expr) PAL_CONSTEXPR_ASSERT_MSG(_expr, "%s", "Unknown")
|
||||
|
||||
#if DEBUG
|
||||
/// Debug build only PAL assert, the typical usage is when make an assertion on a debug-only variables.
|
||||
/// The only difference than PAL assert is it's empty in release mode.
|
||||
#define PAL_DEBUG_BUILD_ONLY_ASSERT(_expr) \
|
||||
do { \
|
||||
PAL_ASSERT(_expr); \
|
||||
} while (false)
|
||||
#else
|
||||
#define PAL_DEBUG_BUILD_ONLY_ASSERT(_expr) ((void)0)
|
||||
#endif
|
||||
|
||||
/// If the expression evaluates to true, then a warning message with the specified reason will be printed via the
|
||||
/// debug print system. A debug break will also be triggered if they're currently enabled for alerts.
|
||||
///
|
||||
/// @note This is the opposite polarity of asserts. The assert macro _asserts_ that the specified condition is true.
|
||||
/// While the alert macro _alerts_ the developer if the specified condition is true.
|
||||
///
|
||||
/// This macro should be used in places where an assert is inappropriate because an error condition is _possible_, but
|
||||
/// not typically expected. For example, asserting that an OS call succeeded should be avoided since there cannot be an
|
||||
/// assumption that it will succeed. Nonetheless, a developer may want to be alerted immediately and dropped into the
|
||||
/// debugger when such a failure occurs.
|
||||
#define PAL_TRIGGER_ALERT(_pFormat, ...) [[unlikely]] \
|
||||
do { \
|
||||
PAL_DPWARN(_pFormat, ##__VA_ARGS__); \
|
||||
if (::Util::IsAssertCategoryEnabled(::Util::AssertCatAlert)) \
|
||||
{ \
|
||||
PAL_DEBUG_BREAK(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
//
|
||||
// This previously said:
|
||||
// if (_expr) [[unlikely]]
|
||||
// {
|
||||
// PAL_TRIGGER_ASSERT(...);
|
||||
// }
|
||||
// However there is a bug in the initial gcc implementation of [[unlikely]] that means you cannot
|
||||
// attach it to a compound statement. So:
|
||||
// 1. we ignore PAL coding standards and don't use a compound statement;
|
||||
// 2. we don't use [[unlikely]] as the expansion of PAL_TRIGGER_ASSERT already has one.
|
||||
#define PAL_ALERT_MSG(_expr, _pReasonFmt, ...) \
|
||||
do { \
|
||||
if (_expr) \
|
||||
PAL_TRIGGER_ALERT("Alert triggered: %s | Reason: " _pReasonFmt, #_expr, ##__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
/// Calls the PAL_ALERT_MSG macro with a generic reason string
|
||||
#define PAL_ALERT(_expr) PAL_ALERT_MSG(_expr, "%s", "Unknown")
|
||||
|
||||
/// Convenience macro that asserts if something has never been tested.
|
||||
#define PAL_NOT_TESTED_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Code Not Tested! | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_NOT_TESTED() PAL_NOT_TESTED_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that asserts if something has not been implemented.
|
||||
#define PAL_NOT_IMPLEMENTED_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Not Implemented! | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_NOT_IMPLEMENTED() PAL_NOT_IMPLEMENTED_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that asserts if an area of code that shouldn't be executed is reached.
|
||||
#define PAL_NEVER_CALLED_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Code should never be called! | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_NEVER_CALLED() PAL_NEVER_CALLED_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that always asserts. Expect this to be used instead of PAL_ASSERT(false).
|
||||
#define PAL_ASSERT_ALWAYS_MSG(_pReasonFmt, ...) PAL_TRIGGER_ASSERT("Unconditional Assert | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_ASSERT_ALWAYS() PAL_ASSERT_ALWAYS_MSG("%s", "Unknown")
|
||||
|
||||
/// Convenience macro that always alerts. Expect this to be used instead of PAL_ALERT(true).
|
||||
#define PAL_ALERT_ALWAYS_MSG(_pReasonFmt, ...) PAL_TRIGGER_ALERT("Unconditional Alert | Reason: " _pReasonFmt, ##__VA_ARGS__)
|
||||
#define PAL_ALERT_ALWAYS() PAL_ALERT_ALWAYS_MSG("%s", "Unknown")
|
||||
|
||||
#else
|
||||
|
||||
#define PAL_ASSERT(_expr) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_CONSTEXPR_ASSERT(_expr) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_ASSERT_MSG(_expr, ...) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_CONSTEXPR_ASSERT_MSG(_expr, ...) PAL_ANALYSIS_ASSUME(_expr)
|
||||
#define PAL_DEBUG_BUILD_ONLY_ASSERT(_expr) ((void)0)
|
||||
#define PAL_ALERT(_expr) ((void)0)
|
||||
#define PAL_ALERT_MSG(_expr, ...) ((void)0)
|
||||
#define PAL_NOT_TESTED() [[unlikely]] ((void)0)
|
||||
#define PAL_NOT_TESTED_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_NOT_IMPLEMENTED() [[unlikely]] ((void)0)
|
||||
#define PAL_NOT_IMPLEMENTED_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_NEVER_CALLED() [[unlikely]] ((void)0)
|
||||
#define PAL_NEVER_CALLED_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_ASSERT_ALWAYS() [[unlikely]] ((void)0)
|
||||
#define PAL_ASSERT_ALWAYS_MSG(...) [[unlikely]] ((void)0)
|
||||
#define PAL_ALERT_ALWAYS() [[unlikely]] ((void)0)
|
||||
#define PAL_ALERT_ALWAYS_MSG(...) [[unlikely]] ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,212 +1,212 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palAutoBuffer.h
|
||||
* @brief PAL utility collection AutoBuffer class definition.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palSpan.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Safe version of C99's variable-length arrays.
|
||||
*
|
||||
* The general idea is that this class encapsulates a variable-length array where we expect the size required to not
|
||||
* exceed the 'defaultCapacity' template parameter most of the time. In those "normal" cases, this buffer will
|
||||
* reference a static array of size 'defaultCapacity', but if the constructor's parameter exceeds defaultCapacity, then
|
||||
* a dynamic array will be allocated from the heap to satisfy the space requirements. The destructor will clean-up any
|
||||
* dynamic allocation made by the constructor.
|
||||
*
|
||||
* This class violates several PAL coding conventions, but for good reason:
|
||||
*
|
||||
* - We have overloaded the [] (array-element-accessor) operator to make using this class just like using a regular
|
||||
* array, which it semantically represents.
|
||||
* - In order to return array elements by-reference instead of by-value, we need to use C++ references in the
|
||||
* overloaded operators because this is required by C++.
|
||||
*
|
||||
* This class __does not__ clear the contents of the static or dynamic arrays, for performance reasons. If a client
|
||||
* needs the buffer to be cleared, it must do the memset itself. (However, if 'Item' is a class type rather than
|
||||
* plain-old-data, the default c'tor will be invoked.)
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename Item, size_t defaultCapacity, typename Allocator>
|
||||
class AutoBuffer
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// The object is initialized to use the static array of items if the required capacity is less than or equal to the
|
||||
/// default capacity. Otherwise, a larger array is allocated on the heap.
|
||||
///
|
||||
/// @param [in] requiredCapacity Number of items actually required (unknown until runtime).
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
AutoBuffer(
|
||||
size_t requiredCapacity,
|
||||
Allocator*const pAllocator)
|
||||
:
|
||||
m_capacity(requiredCapacity),
|
||||
m_pBuffer(reinterpret_cast<Item*>(m_localBuffer)),
|
||||
m_pAllocator(pAllocator)
|
||||
{
|
||||
if (requiredCapacity > defaultCapacity)
|
||||
{
|
||||
// Create dynamically allocated array, by allocating memory and constructing its objects.
|
||||
// On failure, to avoid subtle bugs from misuse, AutoBuffer will be in a zombie state with zero capacity.
|
||||
m_pBuffer = PAL_NEW_ARRAY(Item, requiredCapacity, pAllocator, AllocInternalTemp);
|
||||
if (m_pBuffer == nullptr)
|
||||
{
|
||||
m_capacity = 0;
|
||||
}
|
||||
}
|
||||
else if (!std::is_trivial<Item>::value)
|
||||
{
|
||||
// Explicitly construct all objects of non-trivial type in the local buffer.
|
||||
for (uint32 idx = 0; idx < m_capacity; ++idx)
|
||||
{
|
||||
PAL_PLACEMENT_NEW(m_pBuffer + idx) Item();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
/// Cleans up the dynamically allocated buffer if we allocated one.
|
||||
~AutoBuffer()
|
||||
{
|
||||
if (m_pBuffer != reinterpret_cast<Item*>(m_localBuffer))
|
||||
{
|
||||
// Destory dynamically allocated array, by destroying its objects and freeing memory.
|
||||
PAL_SAFE_DELETE_ARRAY(m_pBuffer, m_pAllocator);
|
||||
}
|
||||
else if (!std::is_trivial<Item>::value)
|
||||
{
|
||||
// Explicitly destroy all objects of non-trivial type from the local buffer.
|
||||
for (uint32 idx = 0; idx < m_capacity; ++idx)
|
||||
{
|
||||
m_pBuffer[idx].~Item();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the capacity of the buffer.
|
||||
///
|
||||
/// Clients can use this function to determine if the constuctor's allocation succeeded.
|
||||
///
|
||||
/// @returns Size of the array in bytes. Should match the requiredCapacity parameter passed to the constructor
|
||||
/// unless a dynamic memory allocation failed.
|
||||
constexpr size_t Capacity() const noexcept { return m_capacity; }
|
||||
|
||||
/// Getter for the size of this buffer, in bytes.
|
||||
constexpr size_t SizeBytes() const noexcept { return (sizeof(Item) * m_capacity); }
|
||||
|
||||
/// Accessor for the nth element of this buffer.
|
||||
const Item& operator[](size_t n) const
|
||||
{
|
||||
PAL_ASSERT(n < m_capacity);
|
||||
return m_pBuffer[n];
|
||||
}
|
||||
|
||||
/// Non-const accessor for the nth element of this buffer.
|
||||
Item& operator[](size_t n)
|
||||
{
|
||||
PAL_ASSERT(n < m_capacity);
|
||||
return m_pBuffer[n];
|
||||
}
|
||||
|
||||
///@{
|
||||
/// Implicitly gets the current contents of the buffer as a Span.
|
||||
///
|
||||
/// @returns The contents of the buffer as a Span; same as Span<T>(Data(), Size()).
|
||||
operator Span<Item>() { return Span<Item>(Data(), Capacity()); }
|
||||
operator Span<const Item>() const { return Span<const Item>(Data(), Capacity()); }
|
||||
///@}
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + Capacity()).
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read & write access.
|
||||
/// The returned pointer contains address of the first element.
|
||||
constexpr Item* Data() noexcept { return m_pBuffer; }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + Capacity()),
|
||||
/// even if the container is empty (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read only access.
|
||||
/// The returned pointer contains address of the first element.
|
||||
constexpr const Item* Data() const noexcept { return m_pBuffer; }
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using Item* as `iterator` and 64-bit size and difference types
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by c++ language features such as range-based-for-loops.
|
||||
using value_type = Item;
|
||||
using reference = Item&;
|
||||
using const_reference = const Item&;
|
||||
using iterator = Item*;
|
||||
using const_iterator = const Item*;
|
||||
using difference_type = ptrdiff_t;
|
||||
using size_type = size_t;
|
||||
|
||||
constexpr iterator begin() noexcept { return Data(); }
|
||||
constexpr iterator end() noexcept { return Data() + Capacity(); }
|
||||
constexpr const_iterator begin() const noexcept { return Data(); }
|
||||
constexpr const_iterator end() const noexcept { return Data() + Capacity(); }
|
||||
constexpr const_iterator cbegin() const noexcept { return Data(); }
|
||||
constexpr const_iterator cend() const noexcept { return Data() + Capacity(); }
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return Capacity() == 0; }
|
||||
constexpr size_type size() const noexcept { return Capacity(); }
|
||||
///@}
|
||||
|
||||
private:
|
||||
// This is a POD-type that exactly fits one Item value.
|
||||
using ValueStorage = typename std::aligned_storage<sizeof(Item), alignof(Item)>::type;
|
||||
|
||||
// Capacity of this buffer (in Items).
|
||||
size_t m_capacity;
|
||||
|
||||
// Buffer pointer this object uses to access the buffer's elements: if the required capacity exceeds the default
|
||||
// capacity, this points to a dynamic array of Items. Otherwise, this points to m_localBuffer.
|
||||
Item* m_pBuffer;
|
||||
|
||||
// Static array providing storage for Items which we expect most objects of this type to end up using.
|
||||
ValueStorage m_localBuffer[defaultCapacity];
|
||||
|
||||
// Allocator for this AutoBuffer.
|
||||
Allocator*const m_pAllocator;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(AutoBuffer);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(AutoBuffer);
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palAutoBuffer.h
|
||||
* @brief PAL utility collection AutoBuffer class definition.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palSpan.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Safe version of C99's variable-length arrays.
|
||||
*
|
||||
* The general idea is that this class encapsulates a variable-length array where we expect the size required to not
|
||||
* exceed the 'defaultCapacity' template parameter most of the time. In those "normal" cases, this buffer will
|
||||
* reference a static array of size 'defaultCapacity', but if the constructor's parameter exceeds defaultCapacity, then
|
||||
* a dynamic array will be allocated from the heap to satisfy the space requirements. The destructor will clean-up any
|
||||
* dynamic allocation made by the constructor.
|
||||
*
|
||||
* This class violates several PAL coding conventions, but for good reason:
|
||||
*
|
||||
* - We have overloaded the [] (array-element-accessor) operator to make using this class just like using a regular
|
||||
* array, which it semantically represents.
|
||||
* - In order to return array elements by-reference instead of by-value, we need to use C++ references in the
|
||||
* overloaded operators because this is required by C++.
|
||||
*
|
||||
* This class __does not__ clear the contents of the static or dynamic arrays, for performance reasons. If a client
|
||||
* needs the buffer to be cleared, it must do the memset itself. (However, if 'Item' is a class type rather than
|
||||
* plain-old-data, the default c'tor will be invoked.)
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename Item, size_t defaultCapacity, typename Allocator>
|
||||
class AutoBuffer
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// The object is initialized to use the static array of items if the required capacity is less than or equal to the
|
||||
/// default capacity. Otherwise, a larger array is allocated on the heap.
|
||||
///
|
||||
/// @param [in] requiredCapacity Number of items actually required (unknown until runtime).
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
AutoBuffer(
|
||||
size_t requiredCapacity,
|
||||
Allocator*const pAllocator)
|
||||
:
|
||||
m_capacity(requiredCapacity),
|
||||
m_pBuffer(reinterpret_cast<Item*>(m_localBuffer)),
|
||||
m_pAllocator(pAllocator)
|
||||
{
|
||||
if (requiredCapacity > defaultCapacity)
|
||||
{
|
||||
// Create dynamically allocated array, by allocating memory and constructing its objects.
|
||||
// On failure, to avoid subtle bugs from misuse, AutoBuffer will be in a zombie state with zero capacity.
|
||||
m_pBuffer = PAL_NEW_ARRAY(Item, requiredCapacity, pAllocator, AllocInternalTemp);
|
||||
if (m_pBuffer == nullptr)
|
||||
{
|
||||
m_capacity = 0;
|
||||
}
|
||||
}
|
||||
else if (!std::is_trivial<Item>::value)
|
||||
{
|
||||
// Explicitly construct all objects of non-trivial type in the local buffer.
|
||||
for (uint32 idx = 0; idx < m_capacity; ++idx)
|
||||
{
|
||||
PAL_PLACEMENT_NEW(m_pBuffer + idx) Item();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
///
|
||||
/// Cleans up the dynamically allocated buffer if we allocated one.
|
||||
~AutoBuffer()
|
||||
{
|
||||
if (m_pBuffer != reinterpret_cast<Item*>(m_localBuffer))
|
||||
{
|
||||
// Destory dynamically allocated array, by destroying its objects and freeing memory.
|
||||
PAL_SAFE_DELETE_ARRAY(m_pBuffer, m_pAllocator);
|
||||
}
|
||||
else if (!std::is_trivial<Item>::value)
|
||||
{
|
||||
// Explicitly destroy all objects of non-trivial type from the local buffer.
|
||||
for (uint32 idx = 0; idx < m_capacity; ++idx)
|
||||
{
|
||||
m_pBuffer[idx].~Item();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the capacity of the buffer.
|
||||
///
|
||||
/// Clients can use this function to determine if the constuctor's allocation succeeded.
|
||||
///
|
||||
/// @returns Size of the array in bytes. Should match the requiredCapacity parameter passed to the constructor
|
||||
/// unless a dynamic memory allocation failed.
|
||||
constexpr size_t Capacity() const noexcept { return m_capacity; }
|
||||
|
||||
/// Getter for the size of this buffer, in bytes.
|
||||
constexpr size_t SizeBytes() const noexcept { return (sizeof(Item) * m_capacity); }
|
||||
|
||||
/// Accessor for the nth element of this buffer.
|
||||
const Item& operator[](size_t n) const
|
||||
{
|
||||
PAL_ASSERT(n < m_capacity);
|
||||
return m_pBuffer[n];
|
||||
}
|
||||
|
||||
/// Non-const accessor for the nth element of this buffer.
|
||||
Item& operator[](size_t n)
|
||||
{
|
||||
PAL_ASSERT(n < m_capacity);
|
||||
return m_pBuffer[n];
|
||||
}
|
||||
|
||||
///@{
|
||||
/// Implicitly gets the current contents of the buffer as a Span.
|
||||
///
|
||||
/// @returns The contents of the buffer as a Span; same as Span<T>(Data(), Size()).
|
||||
operator Span<Item>() { return Span<Item>(Data(), Capacity()); }
|
||||
operator Span<const Item>() const { return Span<const Item>(Data(), Capacity()); }
|
||||
///@}
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + Capacity()).
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read & write access.
|
||||
/// The returned pointer contains address of the first element.
|
||||
constexpr Item* Data() noexcept { return m_pBuffer; }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + Capacity()),
|
||||
/// even if the container is empty (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read only access.
|
||||
/// The returned pointer contains address of the first element.
|
||||
constexpr const Item* Data() const noexcept { return m_pBuffer; }
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using Item* as `iterator` and 64-bit size and difference types
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by c++ language features such as range-based-for-loops.
|
||||
using value_type = Item;
|
||||
using reference = Item&;
|
||||
using const_reference = const Item&;
|
||||
using iterator = Item*;
|
||||
using const_iterator = const Item*;
|
||||
using difference_type = ptrdiff_t;
|
||||
using size_type = size_t;
|
||||
|
||||
constexpr iterator begin() noexcept { return Data(); }
|
||||
constexpr iterator end() noexcept { return Data() + Capacity(); }
|
||||
constexpr const_iterator begin() const noexcept { return Data(); }
|
||||
constexpr const_iterator end() const noexcept { return Data() + Capacity(); }
|
||||
constexpr const_iterator cbegin() const noexcept { return Data(); }
|
||||
constexpr const_iterator cend() const noexcept { return Data() + Capacity(); }
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return Capacity() == 0; }
|
||||
constexpr size_type size() const noexcept { return Capacity(); }
|
||||
///@}
|
||||
|
||||
private:
|
||||
// This is a POD-type that exactly fits one Item value.
|
||||
using ValueStorage = typename std::aligned_storage<sizeof(Item), alignof(Item)>::type;
|
||||
|
||||
// Capacity of this buffer (in Items).
|
||||
size_t m_capacity;
|
||||
|
||||
// Buffer pointer this object uses to access the buffer's elements: if the required capacity exceeds the default
|
||||
// capacity, this points to a dynamic array of Items. Otherwise, this points to m_localBuffer.
|
||||
Item* m_pBuffer;
|
||||
|
||||
// Static array providing storage for Items which we expect most objects of this type to end up using.
|
||||
ValueStorage m_localBuffer[defaultCapacity];
|
||||
|
||||
// Allocator for this AutoBuffer.
|
||||
Allocator*const m_pAllocator;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(AutoBuffer);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(AutoBuffer);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,202 +1,202 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palBuddyAllocator.h
|
||||
* @brief PAL utility BuddyAllocator class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palHashSet.h"
|
||||
#include "palHashMap.h"
|
||||
#include "palMutex.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Buddy Allocator
|
||||
*
|
||||
* Responsible for managing small GPU memory requests by allocating a large base allocation and dividing it into
|
||||
* appropriately sized suballocation blocks.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class BuddyAllocator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
/// @param [in] baseAllocSize The size of the base allocation this buddy allocator suballocates.
|
||||
/// @param [in] minAllocSize The size of the smallest block this buddy allocator can allocate.
|
||||
BuddyAllocator(
|
||||
Allocator* pAllocator,
|
||||
gpusize baseAllocSize,
|
||||
gpusize minAllocSize);
|
||||
~BuddyAllocator();
|
||||
|
||||
/// Initializes the buddy allocator.
|
||||
///
|
||||
/// @returns Success if the buddy allocator has been successfully initialized.
|
||||
Result Init();
|
||||
|
||||
/// Suballocates a block from the base allocation that this buddy allocator manages. Expects @ref ClaimGpuMemory to
|
||||
/// be called directly before it. If a memory manager with multiple buddyAllocators is used, use pattern should
|
||||
/// be: Iterate through buddyAllocators calling ClaimGpuMemory, if one returns @ref Success break out of the loop,
|
||||
/// then call Allocate on that buddyAllocator. If none return @ref Success, then a new buddyAllocator needs to be
|
||||
/// created. The purpose of splitting up buddyAllocator selection and Allocation is to reduce lock contention in
|
||||
/// multithreaded memory managers.
|
||||
///
|
||||
/// @param [in] size The size of the requested suballocation.
|
||||
/// @param [in] alignment The alignment requirements of the requested suballocation.
|
||||
/// @param [out] pOffset The offset the suballocated block starts within the base allocation.
|
||||
///
|
||||
/// @returns Success if the allocation succeeded, @ref ErrorOutOfMemory if there isn't enough system memory to
|
||||
/// fulfill the request, or @ref ErrorOutOfGpuMemory if there isn't a large enough block free in the
|
||||
/// base allocation to fulfill the request.
|
||||
///
|
||||
/// @warning Unless @ref ClaimGpuMemory is called before every single call, the results of @ref Allocate will
|
||||
/// be invalid. If @ref ClaimGpuMemory returns @ref Success, then @ref ErrorOutOfGpuMemory will never be
|
||||
/// returned.
|
||||
Result Allocate(
|
||||
gpusize size,
|
||||
gpusize alignment,
|
||||
gpusize* pOffset);
|
||||
|
||||
/// Frees a previously allocated suballocation.
|
||||
///
|
||||
/// @param [in] offset The offset the suballocated block starts within the base allocation.
|
||||
/// @param [in] size Optional parameter specifying the size of the original allocation.
|
||||
/// @param [in] alignment Optional parameter specifying the alignment of the original allocation.
|
||||
void Free(
|
||||
gpusize offset,
|
||||
gpusize size = 0,
|
||||
gpusize alignment = 0);
|
||||
|
||||
/// Tells whether the base allocation is completely free. If the returned value is true then the caller is safe
|
||||
/// to deallocate the base allocation.
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return (m_numSuballocations == 0);
|
||||
}
|
||||
|
||||
/// Returns the size of the largest allocation that can be suballocated with this buddy allocator.
|
||||
gpusize MaximumAllocationSize() const;
|
||||
|
||||
/// Claims (doesn't allocate) some memory, used to quickly determine if a pool of memory has availible memory.
|
||||
/// Doesn't affect internal state unless Result::Success is returned
|
||||
///
|
||||
/// @param [in] size The size of the requested suballocation.
|
||||
/// @param [in] alignment The alignment requirements of the requested suballocation.
|
||||
///
|
||||
/// @returns Success if there is enough memory in this buddyAllocator to allocate the requested size of memory,
|
||||
/// @ref ErrorOutOfGpuMemory if there is not enough memory
|
||||
///
|
||||
/// @warning Unless this is called to test availible memory before every call to Allocate, then the results will not
|
||||
/// be valid.
|
||||
Result ClaimGpuMemory(
|
||||
gpusize size,
|
||||
gpusize alignment);
|
||||
|
||||
/// Checks if @ref ClaimGpuMemory can actually claim memory, can be used to find the best fit pool. This function
|
||||
/// does NOT acquire a lock on the structures ClaimGpuMemory uses, and does NOT claim or allocate the memory.
|
||||
///
|
||||
/// @param [in] size The size of the requested suballocation.
|
||||
/// @param [in] alignment The alignment requirements of the requested suballocation.
|
||||
/// @param [out] pKval The highest kval that will need to be split will be stored here.
|
||||
///
|
||||
/// @returns Success if there is enough memory in this buddyAllocator to allocate the requested size of memory,
|
||||
/// @ref ErrorOutOfGpuMemory if there is not enough memory
|
||||
///
|
||||
Result CheckIfOpenMemory(
|
||||
gpusize size,
|
||||
gpusize alignment,
|
||||
uint32* pKval);
|
||||
|
||||
private:
|
||||
typedef Util::HashSet<gpusize, Allocator, JenkinsHashFunc> FreeSet;
|
||||
typedef Util::HashMap<gpusize, uint32, Allocator, JenkinsHashFunc> UsedMap;
|
||||
|
||||
Result GetNextFreeBlock(
|
||||
uint32 kval,
|
||||
gpusize* pOffset);
|
||||
|
||||
Result FreeBlock(gpusize offset);
|
||||
|
||||
static constexpr gpusize KvalToSize(uint32 kVal) { return (1ull << kVal); }
|
||||
|
||||
static uint32 SizeToKval(gpusize size) { return Log2(size); }
|
||||
|
||||
Allocator* const m_pAllocator;
|
||||
|
||||
const uint32 m_baseAllocKval;
|
||||
const uint32 m_minKval;
|
||||
|
||||
// Array of hashSets of blocks that are free at each level
|
||||
FreeSet* m_pFreeBlockSets;
|
||||
|
||||
// Hashmap of blocks that are used, key=offset, value=level (kval)
|
||||
UsedMap* m_pUsedBlockMap;
|
||||
// List of the free memory at each level
|
||||
uint32* m_pNumFreeList;
|
||||
// The highest Kval that has at least 1 free block (used in ClaimGpuMemory)
|
||||
uint32 m_highestFreeKval;
|
||||
|
||||
uint32 m_numSuballocations;
|
||||
|
||||
// mutex on altering the numFreeList
|
||||
Util::Mutex m_numFreeMutex;
|
||||
// mutex on the used block map
|
||||
Util::Mutex m_usedBlockMapMutex;
|
||||
// array of mutexes, one for each freeBlockSet
|
||||
Util::Mutex* m_pFreeSetMutexes;
|
||||
// mutex on the freeing. Serialize freeing blocks and don't allow allocating blocks while one is freeing. Based on
|
||||
// testing, applications typically don't try to free and allocate memory at the same time, and almost all of the
|
||||
// memory freeing is done at the end of the application.
|
||||
Util::RWLock m_freeLock;
|
||||
|
||||
// Set to true if ClaimGpuMemory is ever called on this buddyAllocator. This signals to free to not merge blocks
|
||||
// if m_pNumFreeList[kval - m_minKval] = 0
|
||||
bool m_usedClaim;
|
||||
|
||||
// HashSet and HashMap utility functions
|
||||
Result InsertToFreeSet(gpusize offset, uint32 kval);
|
||||
bool GetKvalUsed(gpusize offset, uint32* pKval);
|
||||
Result SetKvalUsed(gpusize offset, uint32 kval);
|
||||
Result PopFromFreeSet(gpusize* pOffset, uint32 kval);
|
||||
bool IsOffsetFree(gpusize offset, uint32 kval);
|
||||
Result RemoveOffsetFromFreeSet(gpusize offset, uint32 kval);
|
||||
Result RemoveOffsetFromUsedMap(gpusize offset);
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(BuddyAllocator);
|
||||
PAL_DISALLOW_DEFAULT_CTOR(BuddyAllocator);
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palBuddyAllocator.h
|
||||
* @brief PAL utility BuddyAllocator class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palHashSet.h"
|
||||
#include "palHashMap.h"
|
||||
#include "palMutex.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Buddy Allocator
|
||||
*
|
||||
* Responsible for managing small GPU memory requests by allocating a large base allocation and dividing it into
|
||||
* appropriately sized suballocation blocks.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class BuddyAllocator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
/// @param [in] baseAllocSize The size of the base allocation this buddy allocator suballocates.
|
||||
/// @param [in] minAllocSize The size of the smallest block this buddy allocator can allocate.
|
||||
BuddyAllocator(
|
||||
Allocator* pAllocator,
|
||||
gpusize baseAllocSize,
|
||||
gpusize minAllocSize);
|
||||
~BuddyAllocator();
|
||||
|
||||
/// Initializes the buddy allocator.
|
||||
///
|
||||
/// @returns Success if the buddy allocator has been successfully initialized.
|
||||
Result Init();
|
||||
|
||||
/// Suballocates a block from the base allocation that this buddy allocator manages. Expects @ref ClaimGpuMemory to
|
||||
/// be called directly before it. If a memory manager with multiple buddyAllocators is used, use pattern should
|
||||
/// be: Iterate through buddyAllocators calling ClaimGpuMemory, if one returns @ref Success break out of the loop,
|
||||
/// then call Allocate on that buddyAllocator. If none return @ref Success, then a new buddyAllocator needs to be
|
||||
/// created. The purpose of splitting up buddyAllocator selection and Allocation is to reduce lock contention in
|
||||
/// multithreaded memory managers.
|
||||
///
|
||||
/// @param [in] size The size of the requested suballocation.
|
||||
/// @param [in] alignment The alignment requirements of the requested suballocation.
|
||||
/// @param [out] pOffset The offset the suballocated block starts within the base allocation.
|
||||
///
|
||||
/// @returns Success if the allocation succeeded, @ref ErrorOutOfMemory if there isn't enough system memory to
|
||||
/// fulfill the request, or @ref ErrorOutOfGpuMemory if there isn't a large enough block free in the
|
||||
/// base allocation to fulfill the request.
|
||||
///
|
||||
/// @warning Unless @ref ClaimGpuMemory is called before every single call, the results of @ref Allocate will
|
||||
/// be invalid. If @ref ClaimGpuMemory returns @ref Success, then @ref ErrorOutOfGpuMemory will never be
|
||||
/// returned.
|
||||
Result Allocate(
|
||||
gpusize size,
|
||||
gpusize alignment,
|
||||
gpusize* pOffset);
|
||||
|
||||
/// Frees a previously allocated suballocation.
|
||||
///
|
||||
/// @param [in] offset The offset the suballocated block starts within the base allocation.
|
||||
/// @param [in] size Optional parameter specifying the size of the original allocation.
|
||||
/// @param [in] alignment Optional parameter specifying the alignment of the original allocation.
|
||||
void Free(
|
||||
gpusize offset,
|
||||
gpusize size = 0,
|
||||
gpusize alignment = 0);
|
||||
|
||||
/// Tells whether the base allocation is completely free. If the returned value is true then the caller is safe
|
||||
/// to deallocate the base allocation.
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return (m_numSuballocations == 0);
|
||||
}
|
||||
|
||||
/// Returns the size of the largest allocation that can be suballocated with this buddy allocator.
|
||||
gpusize MaximumAllocationSize() const;
|
||||
|
||||
/// Claims (doesn't allocate) some memory, used to quickly determine if a pool of memory has availible memory.
|
||||
/// Doesn't affect internal state unless Result::Success is returned
|
||||
///
|
||||
/// @param [in] size The size of the requested suballocation.
|
||||
/// @param [in] alignment The alignment requirements of the requested suballocation.
|
||||
///
|
||||
/// @returns Success if there is enough memory in this buddyAllocator to allocate the requested size of memory,
|
||||
/// @ref ErrorOutOfGpuMemory if there is not enough memory
|
||||
///
|
||||
/// @warning Unless this is called to test availible memory before every call to Allocate, then the results will not
|
||||
/// be valid.
|
||||
Result ClaimGpuMemory(
|
||||
gpusize size,
|
||||
gpusize alignment);
|
||||
|
||||
/// Checks if @ref ClaimGpuMemory can actually claim memory, can be used to find the best fit pool. This function
|
||||
/// does NOT acquire a lock on the structures ClaimGpuMemory uses, and does NOT claim or allocate the memory.
|
||||
///
|
||||
/// @param [in] size The size of the requested suballocation.
|
||||
/// @param [in] alignment The alignment requirements of the requested suballocation.
|
||||
/// @param [out] pKval The highest kval that will need to be split will be stored here.
|
||||
///
|
||||
/// @returns Success if there is enough memory in this buddyAllocator to allocate the requested size of memory,
|
||||
/// @ref ErrorOutOfGpuMemory if there is not enough memory
|
||||
///
|
||||
Result CheckIfOpenMemory(
|
||||
gpusize size,
|
||||
gpusize alignment,
|
||||
uint32* pKval);
|
||||
|
||||
private:
|
||||
typedef Util::HashSet<gpusize, Allocator, JenkinsHashFunc> FreeSet;
|
||||
typedef Util::HashMap<gpusize, uint32, Allocator, JenkinsHashFunc> UsedMap;
|
||||
|
||||
Result GetNextFreeBlock(
|
||||
uint32 kval,
|
||||
gpusize* pOffset);
|
||||
|
||||
Result FreeBlock(gpusize offset);
|
||||
|
||||
static constexpr gpusize KvalToSize(uint32 kVal) { return (1ull << kVal); }
|
||||
|
||||
static uint32 SizeToKval(gpusize size) { return Log2(size); }
|
||||
|
||||
Allocator* const m_pAllocator;
|
||||
|
||||
const uint32 m_baseAllocKval;
|
||||
const uint32 m_minKval;
|
||||
|
||||
// Array of hashSets of blocks that are free at each level
|
||||
FreeSet* m_pFreeBlockSets;
|
||||
|
||||
// Hashmap of blocks that are used, key=offset, value=level (kval)
|
||||
UsedMap* m_pUsedBlockMap;
|
||||
// List of the free memory at each level
|
||||
uint32* m_pNumFreeList;
|
||||
// The highest Kval that has at least 1 free block (used in ClaimGpuMemory)
|
||||
uint32 m_highestFreeKval;
|
||||
|
||||
uint32 m_numSuballocations;
|
||||
|
||||
// mutex on altering the numFreeList
|
||||
Util::Mutex m_numFreeMutex;
|
||||
// mutex on the used block map
|
||||
Util::Mutex m_usedBlockMapMutex;
|
||||
// array of mutexes, one for each freeBlockSet
|
||||
Util::Mutex* m_pFreeSetMutexes;
|
||||
// mutex on the freeing. Serialize freeing blocks and don't allow allocating blocks while one is freeing. Based on
|
||||
// testing, applications typically don't try to free and allocate memory at the same time, and almost all of the
|
||||
// memory freeing is done at the end of the application.
|
||||
Util::RWLock m_freeLock;
|
||||
|
||||
// Set to true if ClaimGpuMemory is ever called on this buddyAllocator. This signals to free to not merge blocks
|
||||
// if m_pNumFreeList[kval - m_minKval] = 0
|
||||
bool m_usedClaim;
|
||||
|
||||
// HashSet and HashMap utility functions
|
||||
Result InsertToFreeSet(gpusize offset, uint32 kval);
|
||||
bool GetKvalUsed(gpusize offset, uint32* pKval);
|
||||
Result SetKvalUsed(gpusize offset, uint32 kval);
|
||||
Result PopFromFreeSet(gpusize* pOffset, uint32 kval);
|
||||
bool IsOffsetFree(gpusize offset, uint32 kval);
|
||||
Result RemoveOffsetFromFreeSet(gpusize offset, uint32 kval);
|
||||
Result RemoveOffsetFromUsedMap(gpusize offset);
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(BuddyAllocator);
|
||||
PAL_DISALLOW_DEFAULT_CTOR(BuddyAllocator);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,364 +1,364 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palDbgPrint.h
|
||||
* @brief Defines PAL utility collection debug print functionality.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#if PAL_ENABLE_LOGGING
|
||||
#include "palDbgLogHelper.h"
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS || PAL_ENABLE_LOGGING)
|
||||
// Forward declarations.
|
||||
class File;
|
||||
enum FileAccessMode : uint32;
|
||||
|
||||
/// Specifies the category of a debug print.
|
||||
///
|
||||
/// Driver developer can enable/disable each category separately through settings.
|
||||
enum DbgPrintCategory : uint32
|
||||
{
|
||||
DbgPrintCatInfoMsg = 0, ///< Generic informational messages.
|
||||
DbgPrintCatWarnMsg, ///< Warning messages.
|
||||
DbgPrintCatErrorMsg, ///< Error messages.
|
||||
DbgPrintCatScMsg, ///< Generic shader compiler messages.
|
||||
DbgPrintCatEventPrintMsg, ///< System event messages.
|
||||
DbgPrintCatEventPrintCallbackMsg, ///< System event messages via callback.
|
||||
DbgPrintCatMsgFile, ///< All Messages via file.
|
||||
DbgPrintCatCount
|
||||
};
|
||||
|
||||
/// Specifies the debug print mode: disabled, print to debugger, or print to file.
|
||||
enum class DbgPrintMode : uint32
|
||||
{
|
||||
Disable, ///< Debug print is ignored.
|
||||
Print, ///< Debug print is routed to the debug window or stdout.
|
||||
File, ///< Debug print is routed to a file.
|
||||
PrintCallback, ///< Debug print is routed to the print callback only
|
||||
};
|
||||
|
||||
/// Flags specifying style controls for a debug print.
|
||||
enum DbgPrintStyle : uint32
|
||||
{
|
||||
DbgPrintStyleDefault = 0x0, ///< Normal mode: has a prefix and a CR-LF.
|
||||
DbgPrintStyleNoPrefix = 0x1, ///< Skip the prefix.
|
||||
DbgPrintStyleNoCrLf = 0x2, ///< Skip the CR-LF.
|
||||
DbgPrintStyleNoPrefixNoCrLf = 0x3, ///< Skip both the prefix and the CR-LF.
|
||||
};
|
||||
|
||||
/// Definition for debug print callback.
|
||||
///
|
||||
/// @param [in] pUserData User data that is installed with the callback for use by the installer.
|
||||
/// @param [in] category Debug print category that the message belongs to.
|
||||
/// @param [in] pText Text data to be printed by the callback.
|
||||
typedef void (PAL_STDCALL *DbgPrintCallbackFunc)(
|
||||
void* pUserdata,
|
||||
DbgPrintCategory category,
|
||||
const char* pText);
|
||||
|
||||
/// Debug print callback struct that bundles the callback function and its userdata pointer.
|
||||
struct DbgPrintCallback
|
||||
{
|
||||
DbgPrintCallbackFunc pCallbackFunc;
|
||||
void* pUserdata;
|
||||
};
|
||||
|
||||
/// Generic debug printf function to be used when the caller wishes to specify the output category and style. Clients
|
||||
/// should use the PAL_DPF macro instead of calling this function directly.
|
||||
///
|
||||
/// @param [in] category Message category (e.g., CS dumps, SC output, etc.).
|
||||
/// @param [in] style Text output style (i.e., has prefix and/or CR-LF).
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
extern void DbgPrintf(
|
||||
DbgPrintCategory category,
|
||||
DbgPrintStyle style,
|
||||
const char* pFormat,
|
||||
...);
|
||||
|
||||
/// Generic printf function to be used when the caller wishes to specify the output category and style, and has
|
||||
/// pre-started the variable arg list (va_list argument instead of ...).
|
||||
///
|
||||
/// @param [in] category Message category (e.g., CS dumps, SC output, etc.).
|
||||
/// @param [in] style Text output style (i.e., has prefix and/or CR-LF).
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
/// @param [in] argList Variable argument list.
|
||||
extern void DbgVPrintf(
|
||||
DbgPrintCategory category,
|
||||
DbgPrintStyle style,
|
||||
const char* pFormat,
|
||||
va_list argList);
|
||||
|
||||
#endif
|
||||
|
||||
#if PAL_ENABLE_PRINTS_ASSERTS
|
||||
/// Sets the debug print mode (output to debugger, write to file, or disabled) for the specified category of messages.
|
||||
///
|
||||
/// Probably controlled by a setting and set during initialization.
|
||||
///
|
||||
/// @param [in] category Message category to control (e.g., CS dumps, SC output, etc.).
|
||||
/// @param [in] mode New mode to be used for this message category (print to file, etc.).
|
||||
extern void SetDbgPrintMode(
|
||||
DbgPrintCategory category,
|
||||
DbgPrintMode mode);
|
||||
|
||||
/// Opens a file that resides in the selected log directory.
|
||||
///
|
||||
/// This function exists in all build configurations.
|
||||
///
|
||||
/// @param [in,out] pFile File object to represent the opened file.
|
||||
/// @param [in] pFilename Filename to open.
|
||||
/// @param [in] flags ORed mask of FileAccessMode values specifying how this file will be accessed.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
extern Result OpenLogFile(
|
||||
File* pFile,
|
||||
const char* pFilename,
|
||||
uint32 flags);
|
||||
|
||||
/// Sets the global debug print callback.
|
||||
///
|
||||
/// @param [in] callback Debug print callback struct that contains the callback function and a userdata pointer
|
||||
extern void SetDbgPrintCallback(
|
||||
const DbgPrintCallback& callback);
|
||||
|
||||
#endif
|
||||
|
||||
// Forward declarations.
|
||||
template<typename CharT> class StringView;
|
||||
|
||||
/// Logs a text string via client callback when provided.
|
||||
///
|
||||
/// @param [in] pClientData Pointer to client-defined data. The pClientData value specified in the pLogCbInfo
|
||||
/// parameter to CreatePlatform() will be passed back to the client on every log callback.
|
||||
/// @param [in] level Log priority level associated with the message.
|
||||
/// @param [in] categoryMask Log category mask that represents what category fields the message relates to.
|
||||
/// @param [in] pFormat Format string for the log message.
|
||||
/// @param [in] args Variable arguments that correspond to the format string.
|
||||
typedef void (PAL_STDCALL *LogCallbackFunc)(
|
||||
void* pClientData,
|
||||
uint32 level,
|
||||
uint64 categoryMask,
|
||||
const char* pFormat,
|
||||
va_list args);
|
||||
|
||||
/// Specifies client-provided logging callbacks. Used as a parameter to Pal::CreatePlatform().
|
||||
///
|
||||
/// @ingroup LibInit
|
||||
struct LogCallbackInfo
|
||||
{
|
||||
void* pClientData; ///< Opaque pointer to data of client's choosing. This pointer will be passed back to
|
||||
/// every @ref LogCallbackFunc call made by PAL.
|
||||
LogCallbackFunc pfnLogCb; ///< Debug print logging callback. @see LogCallbackFunc.
|
||||
};
|
||||
|
||||
/// Compiler-specific wrapper of the standard snprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Snprintf(
|
||||
char* pOutput,
|
||||
size_t bufSize,
|
||||
const char* pFormat,
|
||||
...);
|
||||
|
||||
/// Compiler-specific wrapper of the standard vsnprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string. If buffer is a nullptr it returns the length of the string that would be
|
||||
/// printed had a buffer with enough space been provided.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
/// @param [in] argList variable argument list.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Vsnprintf(
|
||||
char* pOutput,
|
||||
size_t bufSize,
|
||||
const char* pFormat,
|
||||
va_list argList);
|
||||
|
||||
/// Compiler-specific wrapper of the standard snprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Snprintf(
|
||||
wchar_t* pOutput,
|
||||
size_t bufSize,
|
||||
const wchar_t* pFormat,
|
||||
...);
|
||||
|
||||
/// Compiler-specific wrapper of the standard vsnprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string. If buffer is a nullptr it returns the length of the string that would be
|
||||
/// printed had a buffer with enough space been provided.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
/// @param [in] argList variable argument list.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Vsnprintf(
|
||||
wchar_t* pOutput,
|
||||
size_t bufSize,
|
||||
const wchar_t* pFormat,
|
||||
va_list argList);
|
||||
|
||||
/// Copy an arbitrary string into the provided buffer, encoding as necessary to avoid characters that are illegal
|
||||
/// in filenames (assuming the more restrictive Windows rules, even on non-Windows OSs).
|
||||
///
|
||||
/// Any byte that would be illegal is encoded as % then two hex digits, like in a URL.
|
||||
///
|
||||
/// @param [out] pOutput Output string.
|
||||
/// @param bufSize Available space in pOutput.
|
||||
/// @param [in] input Input string
|
||||
/// @param allowSpace Allow (do not % encode) space
|
||||
/// @param allowDirSeparator Allow (do not % encode) / and \ characters
|
||||
///
|
||||
/// @returns Works like C++ standard snprintf:
|
||||
/// - If the provided buffer is big enough, it returns the number of bytes written, excluding the
|
||||
/// terminating \0.
|
||||
/// - If the provided buffer is not big enough, then the result string is truncated to fit, and the
|
||||
/// function returns the number of bytes that would have been written if the buffer had been long
|
||||
/// enough, excluding the terminating \0.
|
||||
/// - Passing 0 buffer length is allowed as a special case of that, and nullptr pOutput is then allowed.
|
||||
extern size_t EncodeAsFilename(
|
||||
char* pOutput,
|
||||
size_t bufSize,
|
||||
const StringView<char>& input,
|
||||
bool allowSpace,
|
||||
bool allowDirSeparator);
|
||||
|
||||
/// Generate a log filename.
|
||||
///
|
||||
/// @param [inout] pFilenameBuffer Buffer to hold the filename.
|
||||
/// @param maxSize Max size of the pFilenameBuffer.
|
||||
/// @param nextPost The next write position.
|
||||
/// @param [in] pExt The filename extension.
|
||||
/// @param logDuplicate Log duplicate objects.
|
||||
extern void GenLogFilename(
|
||||
char* pFilenameBuffer,
|
||||
size_t maxSize,
|
||||
size_t nextPos,
|
||||
const char* const pExt,
|
||||
bool logDuplicate);
|
||||
|
||||
} // Util
|
||||
|
||||
/// PAL_ENABLE_LOGGING enables the new logging code. At this time, both, the current and new logging
|
||||
/// code will be active for development purpose if both macros are enabled.
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS && PAL_ENABLE_LOGGING)
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF ::Util::DbgPrintf
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatInfoMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Info, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatWarnMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Warning, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatErrorMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Error, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
#elif PAL_ENABLE_PRINTS_ASSERTS
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF ::Util::DbgPrintf
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatInfoMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatWarnMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatErrorMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
#elif PAL_ENABLE_LOGGING
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF ::Util::DbgPrintf
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Info, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Warning, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Error, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
#else
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF(...) ((void)0)
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(...) ((void)0)
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(...) ((void)0)
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(...) ((void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palDbgPrint.h
|
||||
* @brief Defines PAL utility collection debug print functionality.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#if PAL_ENABLE_LOGGING
|
||||
#include "palDbgLogHelper.h"
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS || PAL_ENABLE_LOGGING)
|
||||
// Forward declarations.
|
||||
class File;
|
||||
enum FileAccessMode : uint32;
|
||||
|
||||
/// Specifies the category of a debug print.
|
||||
///
|
||||
/// Driver developer can enable/disable each category separately through settings.
|
||||
enum DbgPrintCategory : uint32
|
||||
{
|
||||
DbgPrintCatInfoMsg = 0, ///< Generic informational messages.
|
||||
DbgPrintCatWarnMsg, ///< Warning messages.
|
||||
DbgPrintCatErrorMsg, ///< Error messages.
|
||||
DbgPrintCatScMsg, ///< Generic shader compiler messages.
|
||||
DbgPrintCatEventPrintMsg, ///< System event messages.
|
||||
DbgPrintCatEventPrintCallbackMsg, ///< System event messages via callback.
|
||||
DbgPrintCatMsgFile, ///< All Messages via file.
|
||||
DbgPrintCatCount
|
||||
};
|
||||
|
||||
/// Specifies the debug print mode: disabled, print to debugger, or print to file.
|
||||
enum class DbgPrintMode : uint32
|
||||
{
|
||||
Disable, ///< Debug print is ignored.
|
||||
Print, ///< Debug print is routed to the debug window or stdout.
|
||||
File, ///< Debug print is routed to a file.
|
||||
PrintCallback, ///< Debug print is routed to the print callback only
|
||||
};
|
||||
|
||||
/// Flags specifying style controls for a debug print.
|
||||
enum DbgPrintStyle : uint32
|
||||
{
|
||||
DbgPrintStyleDefault = 0x0, ///< Normal mode: has a prefix and a CR-LF.
|
||||
DbgPrintStyleNoPrefix = 0x1, ///< Skip the prefix.
|
||||
DbgPrintStyleNoCrLf = 0x2, ///< Skip the CR-LF.
|
||||
DbgPrintStyleNoPrefixNoCrLf = 0x3, ///< Skip both the prefix and the CR-LF.
|
||||
};
|
||||
|
||||
/// Definition for debug print callback.
|
||||
///
|
||||
/// @param [in] pUserData User data that is installed with the callback for use by the installer.
|
||||
/// @param [in] category Debug print category that the message belongs to.
|
||||
/// @param [in] pText Text data to be printed by the callback.
|
||||
typedef void (PAL_STDCALL *DbgPrintCallbackFunc)(
|
||||
void* pUserdata,
|
||||
DbgPrintCategory category,
|
||||
const char* pText);
|
||||
|
||||
/// Debug print callback struct that bundles the callback function and its userdata pointer.
|
||||
struct DbgPrintCallback
|
||||
{
|
||||
DbgPrintCallbackFunc pCallbackFunc;
|
||||
void* pUserdata;
|
||||
};
|
||||
|
||||
/// Generic debug printf function to be used when the caller wishes to specify the output category and style. Clients
|
||||
/// should use the PAL_DPF macro instead of calling this function directly.
|
||||
///
|
||||
/// @param [in] category Message category (e.g., CS dumps, SC output, etc.).
|
||||
/// @param [in] style Text output style (i.e., has prefix and/or CR-LF).
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
extern void DbgPrintf(
|
||||
DbgPrintCategory category,
|
||||
DbgPrintStyle style,
|
||||
const char* pFormat,
|
||||
...);
|
||||
|
||||
/// Generic printf function to be used when the caller wishes to specify the output category and style, and has
|
||||
/// pre-started the variable arg list (va_list argument instead of ...).
|
||||
///
|
||||
/// @param [in] category Message category (e.g., CS dumps, SC output, etc.).
|
||||
/// @param [in] style Text output style (i.e., has prefix and/or CR-LF).
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
/// @param [in] argList Variable argument list.
|
||||
extern void DbgVPrintf(
|
||||
DbgPrintCategory category,
|
||||
DbgPrintStyle style,
|
||||
const char* pFormat,
|
||||
va_list argList);
|
||||
|
||||
#endif
|
||||
|
||||
#if PAL_ENABLE_PRINTS_ASSERTS
|
||||
/// Sets the debug print mode (output to debugger, write to file, or disabled) for the specified category of messages.
|
||||
///
|
||||
/// Probably controlled by a setting and set during initialization.
|
||||
///
|
||||
/// @param [in] category Message category to control (e.g., CS dumps, SC output, etc.).
|
||||
/// @param [in] mode New mode to be used for this message category (print to file, etc.).
|
||||
extern void SetDbgPrintMode(
|
||||
DbgPrintCategory category,
|
||||
DbgPrintMode mode);
|
||||
|
||||
/// Opens a file that resides in the selected log directory.
|
||||
///
|
||||
/// This function exists in all build configurations.
|
||||
///
|
||||
/// @param [in,out] pFile File object to represent the opened file.
|
||||
/// @param [in] pFilename Filename to open.
|
||||
/// @param [in] flags ORed mask of FileAccessMode values specifying how this file will be accessed.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
extern Result OpenLogFile(
|
||||
File* pFile,
|
||||
const char* pFilename,
|
||||
uint32 flags);
|
||||
|
||||
/// Sets the global debug print callback.
|
||||
///
|
||||
/// @param [in] callback Debug print callback struct that contains the callback function and a userdata pointer
|
||||
extern void SetDbgPrintCallback(
|
||||
const DbgPrintCallback& callback);
|
||||
|
||||
#endif
|
||||
|
||||
// Forward declarations.
|
||||
template<typename CharT> class StringView;
|
||||
|
||||
/// Logs a text string via client callback when provided.
|
||||
///
|
||||
/// @param [in] pClientData Pointer to client-defined data. The pClientData value specified in the pLogCbInfo
|
||||
/// parameter to CreatePlatform() will be passed back to the client on every log callback.
|
||||
/// @param [in] level Log priority level associated with the message.
|
||||
/// @param [in] categoryMask Log category mask that represents what category fields the message relates to.
|
||||
/// @param [in] pFormat Format string for the log message.
|
||||
/// @param [in] args Variable arguments that correspond to the format string.
|
||||
typedef void (PAL_STDCALL *LogCallbackFunc)(
|
||||
void* pClientData,
|
||||
uint32 level,
|
||||
uint64 categoryMask,
|
||||
const char* pFormat,
|
||||
va_list args);
|
||||
|
||||
/// Specifies client-provided logging callbacks. Used as a parameter to Pal::CreatePlatform().
|
||||
///
|
||||
/// @ingroup LibInit
|
||||
struct LogCallbackInfo
|
||||
{
|
||||
void* pClientData; ///< Opaque pointer to data of client's choosing. This pointer will be passed back to
|
||||
/// every @ref LogCallbackFunc call made by PAL.
|
||||
LogCallbackFunc pfnLogCb; ///< Debug print logging callback. @see LogCallbackFunc.
|
||||
};
|
||||
|
||||
/// Compiler-specific wrapper of the standard snprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Snprintf(
|
||||
char* pOutput,
|
||||
size_t bufSize,
|
||||
const char* pFormat,
|
||||
...);
|
||||
|
||||
/// Compiler-specific wrapper of the standard vsnprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string. If buffer is a nullptr it returns the length of the string that would be
|
||||
/// printed had a buffer with enough space been provided.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
/// @param [in] argList variable argument list.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Vsnprintf(
|
||||
char* pOutput,
|
||||
size_t bufSize,
|
||||
const char* pFormat,
|
||||
va_list argList);
|
||||
|
||||
/// Compiler-specific wrapper of the standard snprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Snprintf(
|
||||
wchar_t* pOutput,
|
||||
size_t bufSize,
|
||||
const wchar_t* pFormat,
|
||||
...);
|
||||
|
||||
/// Compiler-specific wrapper of the standard vsnprintf implementation.
|
||||
///
|
||||
/// @param [out] pOutput Output string. If buffer is a nullptr it returns the length of the string that would be
|
||||
/// printed had a buffer with enough space been provided.
|
||||
/// @param [in] bufSize Available space in pOutput.
|
||||
/// @param [in] pFormat Printf-style format string.
|
||||
/// @param [in] argList variable argument list.
|
||||
///
|
||||
/// @returns The resultant length of the formatted string.
|
||||
extern int32 Vsnprintf(
|
||||
wchar_t* pOutput,
|
||||
size_t bufSize,
|
||||
const wchar_t* pFormat,
|
||||
va_list argList);
|
||||
|
||||
/// Copy an arbitrary string into the provided buffer, encoding as necessary to avoid characters that are illegal
|
||||
/// in filenames (assuming the more restrictive Windows rules, even on non-Windows OSs).
|
||||
///
|
||||
/// Any byte that would be illegal is encoded as % then two hex digits, like in a URL.
|
||||
///
|
||||
/// @param [out] pOutput Output string.
|
||||
/// @param bufSize Available space in pOutput.
|
||||
/// @param [in] input Input string
|
||||
/// @param allowSpace Allow (do not % encode) space
|
||||
/// @param allowDirSeparator Allow (do not % encode) / and \ characters
|
||||
///
|
||||
/// @returns Works like C++ standard snprintf:
|
||||
/// - If the provided buffer is big enough, it returns the number of bytes written, excluding the
|
||||
/// terminating \0.
|
||||
/// - If the provided buffer is not big enough, then the result string is truncated to fit, and the
|
||||
/// function returns the number of bytes that would have been written if the buffer had been long
|
||||
/// enough, excluding the terminating \0.
|
||||
/// - Passing 0 buffer length is allowed as a special case of that, and nullptr pOutput is then allowed.
|
||||
extern size_t EncodeAsFilename(
|
||||
char* pOutput,
|
||||
size_t bufSize,
|
||||
const StringView<char>& input,
|
||||
bool allowSpace,
|
||||
bool allowDirSeparator);
|
||||
|
||||
/// Generate a log filename.
|
||||
///
|
||||
/// @param [inout] pFilenameBuffer Buffer to hold the filename.
|
||||
/// @param maxSize Max size of the pFilenameBuffer.
|
||||
/// @param nextPost The next write position.
|
||||
/// @param [in] pExt The filename extension.
|
||||
/// @param logDuplicate Log duplicate objects.
|
||||
extern void GenLogFilename(
|
||||
char* pFilenameBuffer,
|
||||
size_t maxSize,
|
||||
size_t nextPos,
|
||||
const char* const pExt,
|
||||
bool logDuplicate);
|
||||
|
||||
} // Util
|
||||
|
||||
/// PAL_ENABLE_LOGGING enables the new logging code. At this time, both, the current and new logging
|
||||
/// code will be active for development purpose if both macros are enabled.
|
||||
#if (PAL_ENABLE_PRINTS_ASSERTS && PAL_ENABLE_LOGGING)
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF ::Util::DbgPrintf
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatInfoMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Info, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatWarnMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Warning, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatErrorMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Error, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
#elif PAL_ENABLE_PRINTS_ASSERTS
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF ::Util::DbgPrintf
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatInfoMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatWarnMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgPrintf(::Util::DbgPrintCatErrorMsg, ::Util::DbgPrintStyleDefault, _pFormat " (%s:%d:%s)", \
|
||||
##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
#elif PAL_ENABLE_LOGGING
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF ::Util::DbgPrintf
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Info, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Warning, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(_pFormat, ...) \
|
||||
{ \
|
||||
::Util::DbgLog(::Util::SeverityLevel::Error, ::Util::OriginationType::DebugPrint, \
|
||||
"AMD-PAL", _pFormat " (%s:%d:%s)", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \
|
||||
}
|
||||
#else
|
||||
/// Debug printf macro.
|
||||
#define PAL_DPF(...) ((void)0)
|
||||
/// Debug info printf macro.
|
||||
#define PAL_DPINFO(...) ((void)0)
|
||||
/// Debug warning printf macro.
|
||||
#define PAL_DPWARN(...) ((void)0)
|
||||
/// Debug error printf macro.
|
||||
#define PAL_DPERROR(...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,319 +1,319 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palDeque.h
|
||||
* @brief PAL utility collection Deque and DequeIterator class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palAssert.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
template<typename T, typename Allocator> class Deque;
|
||||
|
||||
/// @internal Private structure used by Deque and its iterators to store chunks of data elements.
|
||||
struct DequeBlockHeader
|
||||
{
|
||||
DequeBlockHeader* pPrev; ///< Pointer to the previous block.
|
||||
DequeBlockHeader* pNext; ///< Pointer to the next block.
|
||||
void* pStart; ///< Pointer to the first element in this block.
|
||||
void* pEnd; ///< Pointer to the last element in this block.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Iterator for traversal of elements in a Deque collection.
|
||||
*
|
||||
* Allows traversal of all elements in a Deque going either forwards or backwards. If you traverse off either end of
|
||||
* the deque, then you must create a new iterator by calling either the Deque's Begin() or End() method.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, typename Allocator>
|
||||
class DequeIterator
|
||||
{
|
||||
public:
|
||||
/// Trivial destructor.
|
||||
~DequeIterator() { }
|
||||
|
||||
/// Returns a pointer to the current element. Will return null if we've gone past the end.
|
||||
T* Get() const { return m_pCurrent; }
|
||||
|
||||
/// Advances the iterator to the next position (move forward).
|
||||
void Next();
|
||||
|
||||
/// Advances the iterator to the previous position (move backward).
|
||||
void Prev();
|
||||
|
||||
/// Check if the element the iterator references is valid.
|
||||
bool IsValid() const { return m_pCurrent != nullptr; }
|
||||
|
||||
private:
|
||||
DequeIterator(const Deque<T, Allocator>* pDeque, DequeBlockHeader* pHeader, T* pCurrent);
|
||||
|
||||
const Deque<T, Allocator>*const m_pDeque; // The Deque we're iterating over.
|
||||
const DequeBlockHeader* m_pCurrentHeader; // The block we're iterating over.
|
||||
T* m_pCurrent; // Pointer to the current element. Null if we've gone past the
|
||||
// end.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(DequeIterator);
|
||||
|
||||
// Although this is a transgression of coding standards, it means that Deque does not need to have a public
|
||||
// interface specifically to implement this class. The added encapsulation this provides is worthwhile.
|
||||
friend class Deque<T, Allocator>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Simple templated deque container - a double-ended queue.
|
||||
*
|
||||
* This is meant for storing elements of an arbitrary (but uniform) type. Operations which this class supports are:
|
||||
*
|
||||
* - Insertion from the front and back.
|
||||
* - Deletion from the front and back.
|
||||
* - Forwards and reverse iteration
|
||||
*
|
||||
* @warning This class is not thread-safe for push, pop, or iteration!
|
||||
*
|
||||
* @note This class is only designed to work with native types and POD-style structures. If it is needed to have a Deque
|
||||
* of complex objects with nontrivial destructors, copy constructors or assign operators, then a specialized
|
||||
* implementation of CleanupElement() will need to be explicitly defined.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, typename Allocator>
|
||||
class Deque
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
Deque(Allocator*const pAllocator, size_t numElementsPerBlock = 256);
|
||||
~Deque();
|
||||
|
||||
/// Returns the number of elements in the deque.
|
||||
size_t NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns an iterator pointing to the first element in the deque.
|
||||
///
|
||||
/// @returns An iterator pointing at the front end of the deque.
|
||||
DequeIterator<T, Allocator> Begin() const { return DequeIterator<T, Allocator>(this, m_pFrontHeader, m_pFront); }
|
||||
|
||||
/// Returns an iterator pointing to the last element in the deque.
|
||||
///
|
||||
/// This is somewhat different from std::deque.End() which returns a pointer to the theoretical object _past_ the
|
||||
/// end of the deque.
|
||||
///
|
||||
/// @returns An iterator pointing at the back end of the deque.
|
||||
DequeIterator<T, Allocator> End() const { return DequeIterator<T, Allocator>(this, m_pBackHeader, m_pBack); }
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
T& At(uint32 index);
|
||||
|
||||
const T& At(uint32 index) const;
|
||||
|
||||
T& operator[](uint32 index);
|
||||
const T& operator[](uint32 index) const;
|
||||
///@}
|
||||
|
||||
/// Returns the object at the front of the deque.
|
||||
///
|
||||
/// @warning This will cause an access violation if called on an empty deque!
|
||||
///
|
||||
/// @returns Reference to the item stored at the front end of the deque.
|
||||
T& Front() const
|
||||
{
|
||||
PAL_ASSERT(m_numElements != 0);
|
||||
return *m_pFront;
|
||||
}
|
||||
|
||||
/// Returns the object at the tail of the deque.
|
||||
///
|
||||
/// @warning This will cause an access violation if called on an empty deque!
|
||||
///
|
||||
/// @returns Reference to the item stored at the back end of the deque.
|
||||
T& Back() const
|
||||
{
|
||||
PAL_ASSERT(m_numElements != 0);
|
||||
return *m_pBack;
|
||||
}
|
||||
|
||||
/// Pushes a copy of the specified item onto the front of the deque.
|
||||
///
|
||||
/// @param [in] data Item to be added to the front of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
Result PushFront(const T& data);
|
||||
|
||||
/// Emplaces a newly constructed item onto the front of the deque.
|
||||
///
|
||||
/// @param [in] args arguments used to construct the new item.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
template<typename... Args>
|
||||
Result EmplaceFront(Args&&... args);
|
||||
|
||||
/// Pushes a copy of the specified item onto the back of the deque.
|
||||
///
|
||||
/// @param [in] data Item to be added to the back of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
Result PushBack(const T& data);
|
||||
|
||||
/// Emplaces a newly constructed item onto the back of the deque.
|
||||
///
|
||||
/// @param [in] args arguments used to construct the new item.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
template<typename... Args>
|
||||
Result EmplaceBack(Args&&... args);
|
||||
|
||||
/// Pops the first item off the front of the deque, returning the popped value.
|
||||
///
|
||||
/// @param [out] pOut Item popped off the front of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque
|
||||
/// is empty.
|
||||
Result PopFront(T* pOut);
|
||||
|
||||
/// Pops the first item off the back of the deque, returning the popped value.
|
||||
///
|
||||
/// @param [out] pOut Item popped off the back of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque
|
||||
/// is empty.
|
||||
Result PopBack(T* pOut);
|
||||
|
||||
private:
|
||||
Result AllocateFront(T**);
|
||||
Result AllocateBack(T**);
|
||||
DequeBlockHeader* AllocateNewBlock();
|
||||
void FreeUnusedBlock(DequeBlockHeader* pHeader);
|
||||
|
||||
// A helper function to avoid duplication in const and non-const versions of At().
|
||||
T& InternalAt(uint32 index) const;
|
||||
|
||||
size_t m_numElements; // Number of elements
|
||||
const size_t m_numElementsPerBlock; // Block granularity when we need to alloc a new one
|
||||
|
||||
DequeBlockHeader* m_pFrontHeader; // First block of data elements, null for empty deques.
|
||||
DequeBlockHeader* m_pBackHeader; // Last block of data elements, null for empty deques/
|
||||
|
||||
T* m_pFront; // First data element, null for empty deques.
|
||||
T* m_pBack; // Last data element, null for empty deques.
|
||||
|
||||
DequeBlockHeader* m_pLazyFreeHeader; // Cached pointer to the most-recently freed block.
|
||||
|
||||
Allocator*const m_pAllocator; // Pointer to the allocator for this deque.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Deque);
|
||||
|
||||
// Although this is a transgression of coding standards, it prevents DequeIterator requiring a public constructor;
|
||||
// constructing a 'bare' DequeIterator (i.e. without calling Deque::GetIterator) can never be a legal operation, so
|
||||
// this means that these two classes are much safer to use.
|
||||
friend class DequeIterator<T, Allocator>;
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, typename Allocator>
|
||||
Deque<T, Allocator>::Deque(
|
||||
Allocator*const pAllocator,
|
||||
size_t numElementsPerBlock)
|
||||
:
|
||||
m_numElements(0),
|
||||
m_numElementsPerBlock(numElementsPerBlock),
|
||||
m_pFrontHeader(nullptr),
|
||||
m_pBackHeader(nullptr),
|
||||
m_pFront(nullptr),
|
||||
m_pBack(nullptr),
|
||||
m_pLazyFreeHeader(nullptr),
|
||||
m_pAllocator(pAllocator)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Frees all of the blocks this object allocated over its lifetime.
|
||||
template<typename T, typename Allocator>
|
||||
Deque<T, Allocator>::~Deque()
|
||||
{
|
||||
if (!std::is_trivial<T>::value)
|
||||
{
|
||||
while (m_pFrontHeader != nullptr)
|
||||
{
|
||||
// Explicitly destroy the removed value since it's non-trivial and advance.
|
||||
// We must destroy all of them in the current block before freeing it.
|
||||
m_pFront->~T();
|
||||
++m_pFront;
|
||||
--m_numElements;
|
||||
|
||||
if ((m_pFront == m_pFrontHeader->pEnd) || (m_numElements == 0))
|
||||
{
|
||||
// Okay, the front block is now empty. Free it and advance to the next block.
|
||||
DequeBlockHeader* pBlockToFree = m_pFrontHeader;
|
||||
m_pFrontHeader = m_pFrontHeader->pNext;
|
||||
PAL_SAFE_FREE(pBlockToFree, m_pAllocator);
|
||||
|
||||
if (m_pFrontHeader != nullptr)
|
||||
{
|
||||
// Fixup to the new block.
|
||||
m_pFront = static_cast<T*>(m_pFrontHeader->pStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Elements are trivial so skip iterating through elements and free each block.
|
||||
while (m_pFrontHeader != nullptr)
|
||||
{
|
||||
DequeBlockHeader* pBlockToFree = m_pFrontHeader;
|
||||
m_pFrontHeader = m_pFrontHeader->pNext;
|
||||
PAL_SAFE_FREE(pBlockToFree, m_pAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pLazyFreeHeader != nullptr)
|
||||
{
|
||||
PAL_SAFE_FREE(m_pLazyFreeHeader, m_pAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palDeque.h
|
||||
* @brief PAL utility collection Deque and DequeIterator class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palAssert.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
template<typename T, typename Allocator> class Deque;
|
||||
|
||||
/// @internal Private structure used by Deque and its iterators to store chunks of data elements.
|
||||
struct DequeBlockHeader
|
||||
{
|
||||
DequeBlockHeader* pPrev; ///< Pointer to the previous block.
|
||||
DequeBlockHeader* pNext; ///< Pointer to the next block.
|
||||
void* pStart; ///< Pointer to the first element in this block.
|
||||
void* pEnd; ///< Pointer to the last element in this block.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Iterator for traversal of elements in a Deque collection.
|
||||
*
|
||||
* Allows traversal of all elements in a Deque going either forwards or backwards. If you traverse off either end of
|
||||
* the deque, then you must create a new iterator by calling either the Deque's Begin() or End() method.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, typename Allocator>
|
||||
class DequeIterator
|
||||
{
|
||||
public:
|
||||
/// Trivial destructor.
|
||||
~DequeIterator() { }
|
||||
|
||||
/// Returns a pointer to the current element. Will return null if we've gone past the end.
|
||||
T* Get() const { return m_pCurrent; }
|
||||
|
||||
/// Advances the iterator to the next position (move forward).
|
||||
void Next();
|
||||
|
||||
/// Advances the iterator to the previous position (move backward).
|
||||
void Prev();
|
||||
|
||||
/// Check if the element the iterator references is valid.
|
||||
bool IsValid() const { return m_pCurrent != nullptr; }
|
||||
|
||||
private:
|
||||
DequeIterator(const Deque<T, Allocator>* pDeque, DequeBlockHeader* pHeader, T* pCurrent);
|
||||
|
||||
const Deque<T, Allocator>*const m_pDeque; // The Deque we're iterating over.
|
||||
const DequeBlockHeader* m_pCurrentHeader; // The block we're iterating over.
|
||||
T* m_pCurrent; // Pointer to the current element. Null if we've gone past the
|
||||
// end.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(DequeIterator);
|
||||
|
||||
// Although this is a transgression of coding standards, it means that Deque does not need to have a public
|
||||
// interface specifically to implement this class. The added encapsulation this provides is worthwhile.
|
||||
friend class Deque<T, Allocator>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Simple templated deque container - a double-ended queue.
|
||||
*
|
||||
* This is meant for storing elements of an arbitrary (but uniform) type. Operations which this class supports are:
|
||||
*
|
||||
* - Insertion from the front and back.
|
||||
* - Deletion from the front and back.
|
||||
* - Forwards and reverse iteration
|
||||
*
|
||||
* @warning This class is not thread-safe for push, pop, or iteration!
|
||||
*
|
||||
* @note This class is only designed to work with native types and POD-style structures. If it is needed to have a Deque
|
||||
* of complex objects with nontrivial destructors, copy constructors or assign operators, then a specialized
|
||||
* implementation of CleanupElement() will need to be explicitly defined.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, typename Allocator>
|
||||
class Deque
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
Deque(Allocator*const pAllocator, size_t numElementsPerBlock = 256);
|
||||
~Deque();
|
||||
|
||||
/// Returns the number of elements in the deque.
|
||||
size_t NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns an iterator pointing to the first element in the deque.
|
||||
///
|
||||
/// @returns An iterator pointing at the front end of the deque.
|
||||
DequeIterator<T, Allocator> Begin() const { return DequeIterator<T, Allocator>(this, m_pFrontHeader, m_pFront); }
|
||||
|
||||
/// Returns an iterator pointing to the last element in the deque.
|
||||
///
|
||||
/// This is somewhat different from std::deque.End() which returns a pointer to the theoretical object _past_ the
|
||||
/// end of the deque.
|
||||
///
|
||||
/// @returns An iterator pointing at the back end of the deque.
|
||||
DequeIterator<T, Allocator> End() const { return DequeIterator<T, Allocator>(this, m_pBackHeader, m_pBack); }
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
T& At(uint32 index);
|
||||
|
||||
const T& At(uint32 index) const;
|
||||
|
||||
T& operator[](uint32 index);
|
||||
const T& operator[](uint32 index) const;
|
||||
///@}
|
||||
|
||||
/// Returns the object at the front of the deque.
|
||||
///
|
||||
/// @warning This will cause an access violation if called on an empty deque!
|
||||
///
|
||||
/// @returns Reference to the item stored at the front end of the deque.
|
||||
T& Front() const
|
||||
{
|
||||
PAL_ASSERT(m_numElements != 0);
|
||||
return *m_pFront;
|
||||
}
|
||||
|
||||
/// Returns the object at the tail of the deque.
|
||||
///
|
||||
/// @warning This will cause an access violation if called on an empty deque!
|
||||
///
|
||||
/// @returns Reference to the item stored at the back end of the deque.
|
||||
T& Back() const
|
||||
{
|
||||
PAL_ASSERT(m_numElements != 0);
|
||||
return *m_pBack;
|
||||
}
|
||||
|
||||
/// Pushes a copy of the specified item onto the front of the deque.
|
||||
///
|
||||
/// @param [in] data Item to be added to the front of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
Result PushFront(const T& data);
|
||||
|
||||
/// Emplaces a newly constructed item onto the front of the deque.
|
||||
///
|
||||
/// @param [in] args arguments used to construct the new item.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
template<typename... Args>
|
||||
Result EmplaceFront(Args&&... args);
|
||||
|
||||
/// Pushes a copy of the specified item onto the back of the deque.
|
||||
///
|
||||
/// @param [in] data Item to be added to the back of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
Result PushBack(const T& data);
|
||||
|
||||
/// Emplaces a newly constructed item onto the back of the deque.
|
||||
///
|
||||
/// @param [in] args arguments used to construct the new item.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
|
||||
/// failed because of an internal failure to allocate system memory.
|
||||
template<typename... Args>
|
||||
Result EmplaceBack(Args&&... args);
|
||||
|
||||
/// Pops the first item off the front of the deque, returning the popped value.
|
||||
///
|
||||
/// @param [out] pOut Item popped off the front of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque
|
||||
/// is empty.
|
||||
Result PopFront(T* pOut);
|
||||
|
||||
/// Pops the first item off the back of the deque, returning the popped value.
|
||||
///
|
||||
/// @param [out] pOut Item popped off the back of the deque.
|
||||
///
|
||||
/// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque
|
||||
/// is empty.
|
||||
Result PopBack(T* pOut);
|
||||
|
||||
private:
|
||||
Result AllocateFront(T**);
|
||||
Result AllocateBack(T**);
|
||||
DequeBlockHeader* AllocateNewBlock();
|
||||
void FreeUnusedBlock(DequeBlockHeader* pHeader);
|
||||
|
||||
// A helper function to avoid duplication in const and non-const versions of At().
|
||||
T& InternalAt(uint32 index) const;
|
||||
|
||||
size_t m_numElements; // Number of elements
|
||||
const size_t m_numElementsPerBlock; // Block granularity when we need to alloc a new one
|
||||
|
||||
DequeBlockHeader* m_pFrontHeader; // First block of data elements, null for empty deques.
|
||||
DequeBlockHeader* m_pBackHeader; // Last block of data elements, null for empty deques/
|
||||
|
||||
T* m_pFront; // First data element, null for empty deques.
|
||||
T* m_pBack; // Last data element, null for empty deques.
|
||||
|
||||
DequeBlockHeader* m_pLazyFreeHeader; // Cached pointer to the most-recently freed block.
|
||||
|
||||
Allocator*const m_pAllocator; // Pointer to the allocator for this deque.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Deque);
|
||||
|
||||
// Although this is a transgression of coding standards, it prevents DequeIterator requiring a public constructor;
|
||||
// constructing a 'bare' DequeIterator (i.e. without calling Deque::GetIterator) can never be a legal operation, so
|
||||
// this means that these two classes are much safer to use.
|
||||
friend class DequeIterator<T, Allocator>;
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, typename Allocator>
|
||||
Deque<T, Allocator>::Deque(
|
||||
Allocator*const pAllocator,
|
||||
size_t numElementsPerBlock)
|
||||
:
|
||||
m_numElements(0),
|
||||
m_numElementsPerBlock(numElementsPerBlock),
|
||||
m_pFrontHeader(nullptr),
|
||||
m_pBackHeader(nullptr),
|
||||
m_pFront(nullptr),
|
||||
m_pBack(nullptr),
|
||||
m_pLazyFreeHeader(nullptr),
|
||||
m_pAllocator(pAllocator)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Frees all of the blocks this object allocated over its lifetime.
|
||||
template<typename T, typename Allocator>
|
||||
Deque<T, Allocator>::~Deque()
|
||||
{
|
||||
if (!std::is_trivial<T>::value)
|
||||
{
|
||||
while (m_pFrontHeader != nullptr)
|
||||
{
|
||||
// Explicitly destroy the removed value since it's non-trivial and advance.
|
||||
// We must destroy all of them in the current block before freeing it.
|
||||
m_pFront->~T();
|
||||
++m_pFront;
|
||||
--m_numElements;
|
||||
|
||||
if ((m_pFront == m_pFrontHeader->pEnd) || (m_numElements == 0))
|
||||
{
|
||||
// Okay, the front block is now empty. Free it and advance to the next block.
|
||||
DequeBlockHeader* pBlockToFree = m_pFrontHeader;
|
||||
m_pFrontHeader = m_pFrontHeader->pNext;
|
||||
PAL_SAFE_FREE(pBlockToFree, m_pAllocator);
|
||||
|
||||
if (m_pFrontHeader != nullptr)
|
||||
{
|
||||
// Fixup to the new block.
|
||||
m_pFront = static_cast<T*>(m_pFrontHeader->pStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Elements are trivial so skip iterating through elements and free each block.
|
||||
while (m_pFrontHeader != nullptr)
|
||||
{
|
||||
DequeBlockHeader* pBlockToFree = m_pFrontHeader;
|
||||
m_pFrontHeader = m_pFrontHeader->pNext;
|
||||
PAL_SAFE_FREE(pBlockToFree, m_pAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pLazyFreeHeader != nullptr)
|
||||
{
|
||||
PAL_SAFE_FREE(m_pLazyFreeHeader, m_pAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,141 +1,141 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palEvent.h
|
||||
* @brief PAL utility collection Event class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palTime.h"
|
||||
#include "palUtil.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Specifies the flags for event.
|
||||
struct EventCreateFlags
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32 manualReset : 1; ///< If true, the event is created as manual reset.
|
||||
uint32 initiallySignaled : 1; ///< If true, the event is created in signaled state.
|
||||
#if defined(_WIN32)
|
||||
uint32 canBeInherited : 1; ///< If true, the event can be inherited by child process, it's
|
||||
/// Windows-specific.
|
||||
uint32 reserved : 29; ///< Reserved for future use.
|
||||
#else
|
||||
uint32 semaphore : 1; ///< If true, provide semaphore-like semantics for reads from the file
|
||||
/// descriptor.
|
||||
uint32 nonBlocking : 1; ///< If true, set the O_NONBLOCK file status flag on the new file descriptor.
|
||||
uint32 closeOnExecute : 1; ///< If true, set the close-on-exec flag for the new file descriptor.
|
||||
uint32 reserved : 27; ///< Reserved for future use.
|
||||
#endif
|
||||
};
|
||||
uint32 u32All; ///< Flags packed as 32-bit uint.
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Synchronization primitive that can either be in the _set_ or _reset_ state.
|
||||
*
|
||||
* Threads can call WaitForEvents() to block waiting for an Event object to be _set_. This is useful for fine-grain
|
||||
* synchronization between threads.
|
||||
*
|
||||
* Event objects start out in the _reset_ state.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
Event();
|
||||
~Event();
|
||||
/// Initializes the event object. Clients must call this before using the Event object.
|
||||
///
|
||||
/// @param flags Event creation flags.
|
||||
/// @param pName Specified the event's name, it's Windows-specific, Windows uses this name to
|
||||
/// uniquely identify fence objects across processes.
|
||||
/// @returns Success if the event was successfully initialized, otherwise an appropriate error code.
|
||||
Result Init(
|
||||
const EventCreateFlags& flags
|
||||
#if defined(_WIN32)
|
||||
,
|
||||
const wchar_t* pName = nullptr
|
||||
#endif
|
||||
);
|
||||
/// Changes the event state to _set_
|
||||
///
|
||||
/// @returns Success unless the Event has not been initialized yet (@ref ErrorUnavailable) or an unexpected internal
|
||||
/// error occured when calling the OS (ErrorUnknown).
|
||||
Result Set() const;
|
||||
|
||||
/// Changes the event state to _reset_.
|
||||
///
|
||||
/// @returns Success unless the Event has not been initialized yet (ErrorUnavailable) or an unexpected
|
||||
/// internal error occured when calling the OS (ErrorUnknown).
|
||||
Result Reset() const;
|
||||
|
||||
/// Waits for the event to enter the _set_ state before returning control to the caller. The event will change to
|
||||
/// the _reset_ state if manualReset was false on initialization.
|
||||
///
|
||||
/// @param [in] timeout Max time to wait, in seconds. If zero, this call will poll the event without blocking.
|
||||
///
|
||||
/// @returns Success if the wait completed successfully or Timeout if the wait did not complete but the operation
|
||||
/// timed out. Otherwise, one of the following errors may be returned:
|
||||
/// + ErrorInvalidValue will be returned if the timeout is negative.
|
||||
/// + ErrorUnknown may be returned if an unexpected internal occurs when calling the OS.
|
||||
Result Wait(fseconds timeout) const;
|
||||
|
||||
#if defined(_WIN32)
|
||||
/// On Windows, a handle to an OS event primitive is a HANDLE, which is just a void*.
|
||||
typedef void* EventHandle;
|
||||
#else
|
||||
/// On Linux, a handle to an OS event primitive is a file descriptor, which is just an int.
|
||||
typedef int32 EventHandle;
|
||||
#endif
|
||||
|
||||
/// Returns a handle to the actual OS event primitive associated with this object.
|
||||
EventHandle GetHandle() const { return m_hEvent; }
|
||||
|
||||
/// Open event handle.
|
||||
Result Open(EventHandle handle, bool isReference);
|
||||
|
||||
/// Constant EventHandle value which represents an invalid event object.
|
||||
static const EventHandle InvalidEvent;
|
||||
|
||||
private:
|
||||
EventHandle m_hEvent; // OS-specific event handle.
|
||||
bool m_isReference; // If true, the event is a global sharing object handle (not a duplicate) which is
|
||||
// imported from external, so it can't be closed in the currect destructor, and can only
|
||||
// be closed by the creater.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Event);
|
||||
};
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palEvent.h
|
||||
* @brief PAL utility collection Event class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palTime.h"
|
||||
#include "palUtil.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Specifies the flags for event.
|
||||
struct EventCreateFlags
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32 manualReset : 1; ///< If true, the event is created as manual reset.
|
||||
uint32 initiallySignaled : 1; ///< If true, the event is created in signaled state.
|
||||
#if defined(_WIN32)
|
||||
uint32 canBeInherited : 1; ///< If true, the event can be inherited by child process, it's
|
||||
/// Windows-specific.
|
||||
uint32 reserved : 29; ///< Reserved for future use.
|
||||
#else
|
||||
uint32 semaphore : 1; ///< If true, provide semaphore-like semantics for reads from the file
|
||||
/// descriptor.
|
||||
uint32 nonBlocking : 1; ///< If true, set the O_NONBLOCK file status flag on the new file descriptor.
|
||||
uint32 closeOnExecute : 1; ///< If true, set the close-on-exec flag for the new file descriptor.
|
||||
uint32 reserved : 27; ///< Reserved for future use.
|
||||
#endif
|
||||
};
|
||||
uint32 u32All; ///< Flags packed as 32-bit uint.
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Synchronization primitive that can either be in the _set_ or _reset_ state.
|
||||
*
|
||||
* Threads can call WaitForEvents() to block waiting for an Event object to be _set_. This is useful for fine-grain
|
||||
* synchronization between threads.
|
||||
*
|
||||
* Event objects start out in the _reset_ state.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
Event();
|
||||
~Event();
|
||||
/// Initializes the event object. Clients must call this before using the Event object.
|
||||
///
|
||||
/// @param flags Event creation flags.
|
||||
/// @param pName Specified the event's name, it's Windows-specific, Windows uses this name to
|
||||
/// uniquely identify fence objects across processes.
|
||||
/// @returns Success if the event was successfully initialized, otherwise an appropriate error code.
|
||||
Result Init(
|
||||
const EventCreateFlags& flags
|
||||
#if defined(_WIN32)
|
||||
,
|
||||
const wchar_t* pName = nullptr
|
||||
#endif
|
||||
);
|
||||
/// Changes the event state to _set_
|
||||
///
|
||||
/// @returns Success unless the Event has not been initialized yet (@ref ErrorUnavailable) or an unexpected internal
|
||||
/// error occured when calling the OS (ErrorUnknown).
|
||||
Result Set() const;
|
||||
|
||||
/// Changes the event state to _reset_.
|
||||
///
|
||||
/// @returns Success unless the Event has not been initialized yet (ErrorUnavailable) or an unexpected
|
||||
/// internal error occured when calling the OS (ErrorUnknown).
|
||||
Result Reset() const;
|
||||
|
||||
/// Waits for the event to enter the _set_ state before returning control to the caller. The event will change to
|
||||
/// the _reset_ state if manualReset was false on initialization.
|
||||
///
|
||||
/// @param [in] timeout Max time to wait, in seconds. If zero, this call will poll the event without blocking.
|
||||
///
|
||||
/// @returns Success if the wait completed successfully or Timeout if the wait did not complete but the operation
|
||||
/// timed out. Otherwise, one of the following errors may be returned:
|
||||
/// + ErrorInvalidValue will be returned if the timeout is negative.
|
||||
/// + ErrorUnknown may be returned if an unexpected internal occurs when calling the OS.
|
||||
Result Wait(fseconds timeout) const;
|
||||
|
||||
#if defined(_WIN32)
|
||||
/// On Windows, a handle to an OS event primitive is a HANDLE, which is just a void*.
|
||||
typedef void* EventHandle;
|
||||
#else
|
||||
/// On Linux, a handle to an OS event primitive is a file descriptor, which is just an int.
|
||||
typedef int32 EventHandle;
|
||||
#endif
|
||||
|
||||
/// Returns a handle to the actual OS event primitive associated with this object.
|
||||
EventHandle GetHandle() const { return m_hEvent; }
|
||||
|
||||
/// Open event handle.
|
||||
Result Open(EventHandle handle, bool isReference);
|
||||
|
||||
/// Constant EventHandle value which represents an invalid event object.
|
||||
static const EventHandle InvalidEvent;
|
||||
|
||||
private:
|
||||
EventHandle m_hEvent; // OS-specific event handle.
|
||||
bool m_isReference; // If true, the event is a global sharing object handle (not a duplicate) which is
|
||||
// imported from external, so it can't be closed in the currect destructor, and can only
|
||||
// be closed by the creater.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Event);
|
||||
};
|
||||
} // Util
|
||||
|
||||
@@ -1,300 +1,300 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palFile.h
|
||||
* @brief PAL utility collection File class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// pal
|
||||
#include "palUtil.h"
|
||||
#include "palInlineFuncs.h"
|
||||
|
||||
// stl
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(_WIN32)
|
||||
/// Macro for wide string literal concatenation.
|
||||
#define PAL_PATH_SEPW L"\\"
|
||||
/// Macro for narrow string literal concatenation.
|
||||
#define PAL_PATH_SEP "\\"
|
||||
#else
|
||||
/// Macro for wide string literal concatenation.
|
||||
#define PAL_PATH_SEPW L"/"
|
||||
/// Macro for narrow string literal concatenation.
|
||||
#define PAL_PATH_SEP "/"
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
/// Wide-character of the platform's prefered path separator.
|
||||
static constexpr wchar_t PathSepW = L'\\';
|
||||
/// Narrow-character of the platform's prefered path separator.
|
||||
static constexpr char PathSep = '\\';
|
||||
#else
|
||||
/// Wide-character of the platform's prefered path separator.
|
||||
static constexpr wchar_t PathSepW = L'/';
|
||||
/// Narrow-character of the platform's prefered path separator.
|
||||
static constexpr char PathSep = '/';
|
||||
#endif
|
||||
|
||||
static constexpr uint32 MaxPathStrLen = 512;
|
||||
static constexpr uint32 MaxFileNameStrLen = 256;
|
||||
|
||||
/// Enumerates access modes that may be required on an opened file.
|
||||
/// Can be bitwise ORed together to specify multiple simultaneous modes.
|
||||
enum FileAccessMode : uint32
|
||||
{
|
||||
FileAccessRead = 0x1, ///< Read access.
|
||||
FileAccessWrite = 0x2, ///< Write access.
|
||||
FileAccessAppend = 0x4, ///< Append access.
|
||||
FileAccessBinary = 0x8, ///< Binary access.
|
||||
FileAccessNoDiscard = 0x10, ///< Don't discard existing file.
|
||||
FileAccessShared = 0x20, ///< Require shared file access (simultaneous reading/writing by more than one process)
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Exposes simple file I/O functionality by encapsulating standard C runtime file I/O functions like fopen,
|
||||
* fwrite, etc.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class File
|
||||
{
|
||||
public:
|
||||
// Platform-agnostic 64-bit stat structure.
|
||||
struct Stat
|
||||
{
|
||||
uint64 size; // Size of the file in bytes.
|
||||
#if PAL_CLIENT_INTERFACE_MAJOR_VERSION < 922
|
||||
uint64 ctime; // Time of creation of the file (not valid on FAT).
|
||||
uint64 atime; // Time of last access to the file (not valid on FAT).
|
||||
uint64 mtime; // Time of last modification to the file.
|
||||
#else
|
||||
std::chrono::system_clock::time_point ctime; // Time of creation of the file (not valid on FAT).
|
||||
std::chrono::system_clock::time_point atime; // Time of last access to the file (not valid on FAT).
|
||||
std::chrono::system_clock::time_point mtime; // Time of last modification to the file.
|
||||
#endif
|
||||
uint32 nlink; // Number of hard links (always 1 on FAT on Windows).
|
||||
uint32 mode; // Bitmask for the file-mode information.
|
||||
uint32 dev; // Drive number of the disk containing the file.
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32 isDir : 1;
|
||||
uint32 isRegular : 1;
|
||||
uint32 reserved : 30;
|
||||
};
|
||||
uint32 u32All;
|
||||
} flags;
|
||||
|
||||
// Common stat members omitted from this structure:
|
||||
// uid, gid, and ino because it's not used on Windows
|
||||
// rdev because it's a duplicate of dev
|
||||
};
|
||||
|
||||
// Where in the file to start seeking from.
|
||||
enum class SeekPosition : int32
|
||||
{
|
||||
// start of the file
|
||||
Start = SEEK_SET,
|
||||
// current file pointer position
|
||||
Current = SEEK_CUR,
|
||||
// end of the file
|
||||
End = SEEK_END
|
||||
};
|
||||
|
||||
File() : m_pFileHandle(nullptr), m_ownsHandle(false) {}
|
||||
|
||||
/// Closes the file if it is still open.
|
||||
~File() { Close(); }
|
||||
|
||||
/// Opens a file stream for read, write or append access.
|
||||
///
|
||||
/// @param [in] pFilename Name of file to open.
|
||||
/// @param [in] accessFlags Bitmask of FileAccessMode values indicating the usage of the file.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Open(const char* pFilename, uint32 accessFlags);
|
||||
|
||||
/// Borrows an externally opened C runtime file handle for use by a File object.
|
||||
///
|
||||
/// The caller is still responsible for closing this handle after the File object is destroyed.
|
||||
///
|
||||
/// @param [in] pFile Externally opened C runtime file handle to borrow.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result FromNative(std::FILE* pFile);
|
||||
|
||||
/// Closes the file handle.
|
||||
void Close();
|
||||
|
||||
/// Writes a stream of bytes to the file.
|
||||
///
|
||||
/// @param [in] pBuffer Byte stream to be written to the file.
|
||||
/// @param [in] bufferSize Number of bytes to write.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Write(const void* pBuffer, size_t bufferSize);
|
||||
|
||||
/// Reads a stream of bytes from the file.
|
||||
///
|
||||
/// @param [out] pBuffer Buffer to be written with data read from file.
|
||||
/// @param [in] bufferSize Size of the output buffer.
|
||||
/// @param [out] pBytesRead Number of bytes actually read (can be null).
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Read(void* pBuffer, size_t bufferSize, size_t* pBytesRead);
|
||||
|
||||
/// Reads a single line of bytes from the file.
|
||||
///
|
||||
/// @param [out] pBuffer Buffer to be written with data read from file.
|
||||
/// @param [in] bufferSize Size of the output buffer.
|
||||
/// @param [out] pBytesRead Number of bytes actually read (can be null).
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result ReadLine(void* pBuffer, size_t bufferSize, size_t* pBytesRead);
|
||||
|
||||
/// Prints a formatted string to the file.
|
||||
///
|
||||
/// @param [in] pFormatStr Printf-style format string.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Printf(const char* pFormatStr, ...) const;
|
||||
|
||||
/// Prints a formatted string to the file.
|
||||
///
|
||||
/// @param [in] pFormatStr Printf-style format string.
|
||||
/// @param [in] argList Variable argument list.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result VPrintf(const char* pFormatStr, va_list argList);
|
||||
|
||||
/// Flushes pending I/O to the file.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Flush() const;
|
||||
|
||||
/// Sets the file position to the beginning of the file.
|
||||
void Rewind();
|
||||
|
||||
/// Sets the position indicator to a new position.
|
||||
///
|
||||
/// @param offset Number of bytes to offset
|
||||
/// @param pos File position to seek from
|
||||
void Seek(int64 offset, SeekPosition pos);
|
||||
|
||||
/// Sets the position indicator to a new position relative to the beginning of the file.
|
||||
///
|
||||
/// @param offset Number of bytes to offset
|
||||
void Seek(size_t offset) { Seek(offset, SeekPosition::Start); }
|
||||
|
||||
/// Sets the position indicator to a new position relative to the end of the file
|
||||
///
|
||||
/// @param offset Number of bytes to offset
|
||||
void Rseek(size_t offset) { Seek(-static_cast<int64>(offset), SeekPosition::End); }
|
||||
|
||||
/// Sets the file position to the end of the file.
|
||||
void FastForward() { Rseek(0); }
|
||||
|
||||
/// Returns true if the file is presently open.
|
||||
bool IsOpen() const { return (m_pFileHandle != nullptr); }
|
||||
|
||||
/// Gets the size of the file contents in bytes
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to check.
|
||||
///
|
||||
/// @returns Size of the file in bytes, or std::numeric_limits<size_t>::max() on failure.
|
||||
static size_t GetFileSize(const char* pFilename);
|
||||
|
||||
/// Checks if a file with the specified name exists.
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to check.
|
||||
///
|
||||
/// @returns True if the specified file exists.
|
||||
static bool Exists(const char* pFilename);
|
||||
|
||||
/// Platform-agnostic 64-bit stat() function.
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to check.
|
||||
/// @param [out] pStatus The status of that file, if it exists.
|
||||
///
|
||||
/// @returns Success if the structure was retrieved, error otherwise.
|
||||
static Result GetStat(const char* pFilename, Stat* pStatus);
|
||||
|
||||
/// Removes/erases a file, if it exists.
|
||||
///
|
||||
/// @param [in] pFilename Name of file to remove.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
static Result Remove(const char* pFilename);
|
||||
|
||||
/// Reads a file into memory.
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to read.
|
||||
/// @param [in] pData Buffer where the file contents are written to.
|
||||
/// @param [in] dataSize Size of the buffer in bytes.
|
||||
/// @param [out] pBytesRead Number of bytes successfully read into the input buffer (can be null).
|
||||
/// @param [in] binary True for binary mode, false for text. Defaults to binary.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
///
|
||||
/// @note The input buffer must be large enough to hold the file's contents. If the buffer is larger than the file,
|
||||
/// then the region of the buffer beyond the file size is _not_ modified by this function. It is the caller's
|
||||
/// responsibility to _not_ read uninitialized portions of the supplied buffer after this call returns.
|
||||
///
|
||||
/// @note In binary mode, the number of bytes read is equal to the file size in bytes upon a successful return.
|
||||
/// In text mode, newline conversion is performed on Windows, in which case the number of bytes read may not equal
|
||||
/// the file size in bytes.
|
||||
///
|
||||
/// @note In text mode, should the caller treat the resulting data as a C string, it is the caller's responsibility
|
||||
/// to null-terminate the buffer.
|
||||
static Result ReadFile(
|
||||
const char* pFilename,
|
||||
void* pData,
|
||||
size_t dataSize,
|
||||
size_t* pBytesRead = nullptr,
|
||||
bool binary = true);
|
||||
|
||||
/// Gets the handle associated with this file.
|
||||
///
|
||||
/// @returns A pointer to the file handle
|
||||
const std::FILE* GetHandle() const { return m_pFileHandle; }
|
||||
|
||||
private:
|
||||
std::FILE* m_pFileHandle;
|
||||
bool m_ownsHandle; // This object owns the file handle and will close it on destruction.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(File);
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palFile.h
|
||||
* @brief PAL utility collection File class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// pal
|
||||
#include "palUtil.h"
|
||||
#include "palInlineFuncs.h"
|
||||
|
||||
// stl
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(_WIN32)
|
||||
/// Macro for wide string literal concatenation.
|
||||
#define PAL_PATH_SEPW L"\\"
|
||||
/// Macro for narrow string literal concatenation.
|
||||
#define PAL_PATH_SEP "\\"
|
||||
#else
|
||||
/// Macro for wide string literal concatenation.
|
||||
#define PAL_PATH_SEPW L"/"
|
||||
/// Macro for narrow string literal concatenation.
|
||||
#define PAL_PATH_SEP "/"
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
/// Wide-character of the platform's prefered path separator.
|
||||
static constexpr wchar_t PathSepW = L'\\';
|
||||
/// Narrow-character of the platform's prefered path separator.
|
||||
static constexpr char PathSep = '\\';
|
||||
#else
|
||||
/// Wide-character of the platform's prefered path separator.
|
||||
static constexpr wchar_t PathSepW = L'/';
|
||||
/// Narrow-character of the platform's prefered path separator.
|
||||
static constexpr char PathSep = '/';
|
||||
#endif
|
||||
|
||||
static constexpr uint32 MaxPathStrLen = 512;
|
||||
static constexpr uint32 MaxFileNameStrLen = 256;
|
||||
|
||||
/// Enumerates access modes that may be required on an opened file.
|
||||
/// Can be bitwise ORed together to specify multiple simultaneous modes.
|
||||
enum FileAccessMode : uint32
|
||||
{
|
||||
FileAccessRead = 0x1, ///< Read access.
|
||||
FileAccessWrite = 0x2, ///< Write access.
|
||||
FileAccessAppend = 0x4, ///< Append access.
|
||||
FileAccessBinary = 0x8, ///< Binary access.
|
||||
FileAccessNoDiscard = 0x10, ///< Don't discard existing file.
|
||||
FileAccessShared = 0x20, ///< Require shared file access (simultaneous reading/writing by more than one process)
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Exposes simple file I/O functionality by encapsulating standard C runtime file I/O functions like fopen,
|
||||
* fwrite, etc.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class File
|
||||
{
|
||||
public:
|
||||
// Platform-agnostic 64-bit stat structure.
|
||||
struct Stat
|
||||
{
|
||||
uint64 size; // Size of the file in bytes.
|
||||
#if PAL_CLIENT_INTERFACE_MAJOR_VERSION < 922
|
||||
uint64 ctime; // Time of creation of the file (not valid on FAT).
|
||||
uint64 atime; // Time of last access to the file (not valid on FAT).
|
||||
uint64 mtime; // Time of last modification to the file.
|
||||
#else
|
||||
std::chrono::system_clock::time_point ctime; // Time of creation of the file (not valid on FAT).
|
||||
std::chrono::system_clock::time_point atime; // Time of last access to the file (not valid on FAT).
|
||||
std::chrono::system_clock::time_point mtime; // Time of last modification to the file.
|
||||
#endif
|
||||
uint32 nlink; // Number of hard links (always 1 on FAT on Windows).
|
||||
uint32 mode; // Bitmask for the file-mode information.
|
||||
uint32 dev; // Drive number of the disk containing the file.
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32 isDir : 1;
|
||||
uint32 isRegular : 1;
|
||||
uint32 reserved : 30;
|
||||
};
|
||||
uint32 u32All;
|
||||
} flags;
|
||||
|
||||
// Common stat members omitted from this structure:
|
||||
// uid, gid, and ino because it's not used on Windows
|
||||
// rdev because it's a duplicate of dev
|
||||
};
|
||||
|
||||
// Where in the file to start seeking from.
|
||||
enum class SeekPosition : int32
|
||||
{
|
||||
// start of the file
|
||||
Start = SEEK_SET,
|
||||
// current file pointer position
|
||||
Current = SEEK_CUR,
|
||||
// end of the file
|
||||
End = SEEK_END
|
||||
};
|
||||
|
||||
File() : m_pFileHandle(nullptr), m_ownsHandle(false) {}
|
||||
|
||||
/// Closes the file if it is still open.
|
||||
~File() { Close(); }
|
||||
|
||||
/// Opens a file stream for read, write or append access.
|
||||
///
|
||||
/// @param [in] pFilename Name of file to open.
|
||||
/// @param [in] accessFlags Bitmask of FileAccessMode values indicating the usage of the file.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Open(const char* pFilename, uint32 accessFlags);
|
||||
|
||||
/// Borrows an externally opened C runtime file handle for use by a File object.
|
||||
///
|
||||
/// The caller is still responsible for closing this handle after the File object is destroyed.
|
||||
///
|
||||
/// @param [in] pFile Externally opened C runtime file handle to borrow.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result FromNative(std::FILE* pFile);
|
||||
|
||||
/// Closes the file handle.
|
||||
void Close();
|
||||
|
||||
/// Writes a stream of bytes to the file.
|
||||
///
|
||||
/// @param [in] pBuffer Byte stream to be written to the file.
|
||||
/// @param [in] bufferSize Number of bytes to write.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Write(const void* pBuffer, size_t bufferSize);
|
||||
|
||||
/// Reads a stream of bytes from the file.
|
||||
///
|
||||
/// @param [out] pBuffer Buffer to be written with data read from file.
|
||||
/// @param [in] bufferSize Size of the output buffer.
|
||||
/// @param [out] pBytesRead Number of bytes actually read (can be null).
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Read(void* pBuffer, size_t bufferSize, size_t* pBytesRead);
|
||||
|
||||
/// Reads a single line of bytes from the file.
|
||||
///
|
||||
/// @param [out] pBuffer Buffer to be written with data read from file.
|
||||
/// @param [in] bufferSize Size of the output buffer.
|
||||
/// @param [out] pBytesRead Number of bytes actually read (can be null).
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result ReadLine(void* pBuffer, size_t bufferSize, size_t* pBytesRead);
|
||||
|
||||
/// Prints a formatted string to the file.
|
||||
///
|
||||
/// @param [in] pFormatStr Printf-style format string.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Printf(const char* pFormatStr, ...) const;
|
||||
|
||||
/// Prints a formatted string to the file.
|
||||
///
|
||||
/// @param [in] pFormatStr Printf-style format string.
|
||||
/// @param [in] argList Variable argument list.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result VPrintf(const char* pFormatStr, va_list argList);
|
||||
|
||||
/// Flushes pending I/O to the file.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
Result Flush() const;
|
||||
|
||||
/// Sets the file position to the beginning of the file.
|
||||
void Rewind();
|
||||
|
||||
/// Sets the position indicator to a new position.
|
||||
///
|
||||
/// @param offset Number of bytes to offset
|
||||
/// @param pos File position to seek from
|
||||
void Seek(int64 offset, SeekPosition pos);
|
||||
|
||||
/// Sets the position indicator to a new position relative to the beginning of the file.
|
||||
///
|
||||
/// @param offset Number of bytes to offset
|
||||
void Seek(size_t offset) { Seek(offset, SeekPosition::Start); }
|
||||
|
||||
/// Sets the position indicator to a new position relative to the end of the file
|
||||
///
|
||||
/// @param offset Number of bytes to offset
|
||||
void Rseek(size_t offset) { Seek(-static_cast<int64>(offset), SeekPosition::End); }
|
||||
|
||||
/// Sets the file position to the end of the file.
|
||||
void FastForward() { Rseek(0); }
|
||||
|
||||
/// Returns true if the file is presently open.
|
||||
bool IsOpen() const { return (m_pFileHandle != nullptr); }
|
||||
|
||||
/// Gets the size of the file contents in bytes
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to check.
|
||||
///
|
||||
/// @returns Size of the file in bytes, or std::numeric_limits<size_t>::max() on failure.
|
||||
static size_t GetFileSize(const char* pFilename);
|
||||
|
||||
/// Checks if a file with the specified name exists.
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to check.
|
||||
///
|
||||
/// @returns True if the specified file exists.
|
||||
static bool Exists(const char* pFilename);
|
||||
|
||||
/// Platform-agnostic 64-bit stat() function.
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to check.
|
||||
/// @param [out] pStatus The status of that file, if it exists.
|
||||
///
|
||||
/// @returns Success if the structure was retrieved, error otherwise.
|
||||
static Result GetStat(const char* pFilename, Stat* pStatus);
|
||||
|
||||
/// Removes/erases a file, if it exists.
|
||||
///
|
||||
/// @param [in] pFilename Name of file to remove.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
static Result Remove(const char* pFilename);
|
||||
|
||||
/// Reads a file into memory.
|
||||
///
|
||||
/// @param [in] pFilename Name of the file to read.
|
||||
/// @param [in] pData Buffer where the file contents are written to.
|
||||
/// @param [in] dataSize Size of the buffer in bytes.
|
||||
/// @param [out] pBytesRead Number of bytes successfully read into the input buffer (can be null).
|
||||
/// @param [in] binary True for binary mode, false for text. Defaults to binary.
|
||||
///
|
||||
/// @returns Success if successful, otherwise an appropriate error.
|
||||
///
|
||||
/// @note The input buffer must be large enough to hold the file's contents. If the buffer is larger than the file,
|
||||
/// then the region of the buffer beyond the file size is _not_ modified by this function. It is the caller's
|
||||
/// responsibility to _not_ read uninitialized portions of the supplied buffer after this call returns.
|
||||
///
|
||||
/// @note In binary mode, the number of bytes read is equal to the file size in bytes upon a successful return.
|
||||
/// In text mode, newline conversion is performed on Windows, in which case the number of bytes read may not equal
|
||||
/// the file size in bytes.
|
||||
///
|
||||
/// @note In text mode, should the caller treat the resulting data as a C string, it is the caller's responsibility
|
||||
/// to null-terminate the buffer.
|
||||
static Result ReadFile(
|
||||
const char* pFilename,
|
||||
void* pData,
|
||||
size_t dataSize,
|
||||
size_t* pBytesRead = nullptr,
|
||||
bool binary = true);
|
||||
|
||||
/// Gets the handle associated with this file.
|
||||
///
|
||||
/// @returns A pointer to the file handle
|
||||
const std::FILE* GetHandle() const { return m_pFileHandle; }
|
||||
|
||||
private:
|
||||
std::FILE* m_pFileHandle;
|
||||
bool m_ownsHandle; // This object owns the file handle and will close it on destruction.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(File);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,143 +1,137 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashMap.h
|
||||
* @brief PAL utility collection HashMap class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBase.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Encapsulates one key/value pair in a hash map.
|
||||
template<typename Key, typename Value>
|
||||
struct HashMapEntry
|
||||
{
|
||||
Key key; ///< Hash map entry key.
|
||||
Value value; ///< Hash map entry value.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Templated hash map container.
|
||||
*
|
||||
* This container is meant for storing elements of an arbitrary (but uniform) key/value type. Supported operations:
|
||||
*
|
||||
* - Searching
|
||||
* - Insertion
|
||||
* - Deletion
|
||||
* - Iteration
|
||||
*
|
||||
* HashFunc is a functor for hashing keys. Built-in choices for HashFunc are:
|
||||
*
|
||||
* - DefaultHashFunc: Good choice when the key is a pointer.
|
||||
* - JenkinsHashFunc: Good choice when the key is arbitrary binary data.
|
||||
* - StringJenkinsHashFunc: Good choice when the key is a C-style string.
|
||||
*
|
||||
* EqualFunc is a functor for comparing keys. Built-in choices for EqualFunc are:
|
||||
*
|
||||
* - DefaultEqualFunc: Determines keys are equal by bitwise comparison.
|
||||
* - StringEqualFunc: Treats keys as a char* and compares them as C-style strings.
|
||||
*
|
||||
* @warning This class is not thread-safe for Insert, FindAllocate, Erase, or iteration!
|
||||
* @warning Init() must be called before using this container. Begin() and Reset() can be safely called before
|
||||
* initialization and Begin() will always return an iterator that points to null.
|
||||
*
|
||||
* For more details please refer to @ref HashBase.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc = DefaultHashFunc,
|
||||
template<typename> class EqualFunc = DefaultEqualFunc,
|
||||
typename AllocFunc = HashAllocator<Allocator>,
|
||||
size_t GroupSize = PAL_CACHE_LINE_BYTES * 2>
|
||||
class HashMap : public HashBase<Key, HashMapEntry<Key, Value>, Allocator, HashFunc<Key>, EqualFunc<Key>, AllocFunc, GroupSize>
|
||||
{
|
||||
public:
|
||||
/// Convenience typedef for a templated entry of this hash map.
|
||||
typedef HashMapEntry<Key, Value> Entry;
|
||||
|
||||
/// @internal Constructor
|
||||
///
|
||||
/// @param [in] numBuckets Number of buckets to allocate for this hash container. The initial hash container will
|
||||
/// take (buckets * GroupSize) bytes.
|
||||
/// @param [in] pAllocator Pointer to an allocator that will create system memory requested by this hash container.
|
||||
explicit HashMap(uint32 numBuckets, Allocator*const pAllocator): Base::HashBase(numBuckets, pAllocator) { }
|
||||
virtual ~HashMap() { }
|
||||
|
||||
/// Finds a given entry; if no entry was found, allocate it.
|
||||
///
|
||||
/// @param [in] key Key to search for.
|
||||
/// @param [out] pExisted True if an entry for the specified key existed before this call was made. False indicates
|
||||
/// that a new entry was allocated as a result of this call.
|
||||
/// @param [out] ppValue Readable/writeable value in the hash map corresponding to the specified key.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully, or @ref ErrorOutOfMemory if the operation failed
|
||||
/// because an internal memory allocation failed.
|
||||
Result FindAllocate(const Key& key, bool* pExisted, Value** ppValue);
|
||||
|
||||
/// Gets a pointer to the value that matches the specified key.
|
||||
///
|
||||
/// @param [in] key Key to search for.
|
||||
///
|
||||
/// @returns A pointer to the value that matches the specified key or null if an entry for the key does not exist.
|
||||
Value* FindKey(const Key& key) const;
|
||||
|
||||
/// Inserts a key/value pair entry if the key doesn't already exist in the hash map.
|
||||
///
|
||||
/// @warning No action will be taken if an entry matching this key already exists, even if the specified value
|
||||
/// differs from the current value stored in the entry matching the specified key.
|
||||
///
|
||||
/// @param [in] key Key of the new entry to insert.
|
||||
/// @param [in] value Value of the new entry to insert.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully, or @ref ErrorOutOfMemory if the operation failed
|
||||
/// because an internal memory allocation failed.
|
||||
Result Insert(const Key& key, const Value& value);
|
||||
|
||||
/// Removes an entry that matches the specified key.
|
||||
///
|
||||
/// @param [in] key Key of the entry to erase.
|
||||
///
|
||||
/// @returns True if the erase completed successfully, false if an entry for this key did not exist.
|
||||
bool Erase(const Key& key);
|
||||
|
||||
private:
|
||||
// Typedef for the specialized 'HashBase' object we're inheriting from so we can use properly qualified names when
|
||||
// accessing members of HashBase.
|
||||
typedef HashBase<Key, HashMapEntry<Key, Value>, Allocator, HashFunc<Key>, EqualFunc<Key>, AllocFunc, GroupSize> Base;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(HashMap);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(HashMap);
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashMap.h
|
||||
* @brief PAL utility collection HashMap class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBase.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Encapsulates one key/value pair in a hash map.
|
||||
template<typename Key, typename Value>
|
||||
struct HashMapEntry
|
||||
{
|
||||
Key key; ///< Hash map entry key.
|
||||
Value value; ///< Hash map entry value.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Templated hash map container.
|
||||
*
|
||||
* This container is meant for storing elements of an arbitrary (but uniform) key/value type. Supported operations:
|
||||
*
|
||||
* - Searching
|
||||
* - Insertion
|
||||
* - Deletion
|
||||
* - Iteration
|
||||
*
|
||||
* HashFunc is a functor for hashing keys. Built-in choices for HashFunc are:
|
||||
*
|
||||
* - DefaultHashFunc: Default hash function, selects best hash function based on type of key.
|
||||
* - PointerHashFunc: Good choice when the key is a pointer.
|
||||
* - JenkinsHashFunc: Good choice when the key is arbitrary binary data.
|
||||
* - StringJenkinsHashFunc: Good choice when the key is a C-style string.
|
||||
*
|
||||
* EqualFunc is a functor for comparing keys. Built-in choices for EqualFunc are:
|
||||
*
|
||||
* - DefaultEqualFunc: Determines keys are equal by bitwise comparison.
|
||||
* - StringEqualFunc: Treats keys as a char* and compares them as C-style strings.
|
||||
*
|
||||
* @warning This class is not thread-safe for Insert, FindAllocate, Erase, or iteration!
|
||||
* @warning Init() must be called before using this container. Begin() and Reset() can be safely called before
|
||||
* initialization and Begin() will always return an iterator that points to null.
|
||||
*
|
||||
* For more details please refer to @ref HashBase.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc = DefaultHashFunc,
|
||||
template<typename> class EqualFunc = DefaultEqualFunc,
|
||||
typename AllocFunc = HashAllocator<Allocator>,
|
||||
size_t GroupSize = PAL_CACHE_LINE_BYTES * 2>
|
||||
class HashMap : public HashBase<Key, HashMapEntry<Key, Value>, Allocator, HashFunc<Key>, EqualFunc<Key>, AllocFunc, GroupSize>
|
||||
{
|
||||
public:
|
||||
/// Convenience typedef for a templated entry of this hash map.
|
||||
typedef HashMapEntry<Key, Value> Entry;
|
||||
|
||||
/// @internal Constructor
|
||||
///
|
||||
/// @param [in] numBuckets Number of buckets to allocate for this hash container. The initial hash container will
|
||||
/// take (buckets * GroupSize) bytes.
|
||||
/// @param [in] pAllocator Pointer to an allocator that will create system memory requested by this hash container.
|
||||
explicit HashMap(uint32 numBuckets, Allocator*const pAllocator): Base::HashBase(numBuckets, pAllocator) { }
|
||||
~HashMap() { }
|
||||
|
||||
/// Finds a given entry; if no entry was found, allocate it.
|
||||
///
|
||||
/// @param [in] key Key to search for.
|
||||
/// @param [out] pExisted True if an entry for the specified key existed before this call was made. False indicates
|
||||
/// that a new entry was allocated as a result of this call.
|
||||
/// @param [out] ppValue Readable/writeable value in the hash map corresponding to the specified key.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully, or @ref ErrorOutOfMemory if the operation failed
|
||||
/// because an internal memory allocation failed.
|
||||
Result FindAllocate(const Key& key, bool* pExisted, Value** ppValue);
|
||||
|
||||
/// Gets a pointer to the value that matches the specified key.
|
||||
///
|
||||
/// @param [in] key Key to search for.
|
||||
///
|
||||
/// @returns A pointer to the value that matches the specified key or null if an entry for the key does not exist.
|
||||
Value* FindKey(const Key& key) const;
|
||||
|
||||
/// Inserts a key/value pair entry if the key doesn't already exist in the hash map.
|
||||
///
|
||||
/// @warning No action will be taken if an entry matching this key already exists, even if the specified value
|
||||
/// differs from the current value stored in the entry matching the specified key.
|
||||
///
|
||||
/// @param [in] key Key of the new entry to insert.
|
||||
/// @param [in] value Value of the new entry to insert.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully, or @ref ErrorOutOfMemory if the operation failed
|
||||
/// because an internal memory allocation failed.
|
||||
Result Insert(const Key& key, const Value& value);
|
||||
|
||||
private:
|
||||
// Typedef for the specialized 'HashBase' object we're inheriting from so we can use properly qualified names when
|
||||
// accessing members of HashBase.
|
||||
typedef HashBase<Key, HashMapEntry<Key, Value>, Allocator, HashFunc<Key>, EqualFunc<Key>, AllocFunc, GroupSize> Base;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(HashMap);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(HashMap);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,250 +1,114 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashMapImpl.h
|
||||
* @brief PAL utility collection HashMap class implementation.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBaseImpl.h"
|
||||
#include "palHashMap.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// =====================================================================================================================
|
||||
// Gets a pointer to the value that matches the key. If the key is not present, a pointer to empty space for the value
|
||||
// is returned.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::FindAllocate(
|
||||
const Key& key, // Key to search for.
|
||||
bool* pExisted, // [out] True if a matching key was found.
|
||||
Value** ppValue) // [out] Pointer to the value entry of the hash map's entry for the specified key.
|
||||
{
|
||||
PAL_ASSERT(pExisted != nullptr);
|
||||
PAL_ASSERT(ppValue != nullptr);
|
||||
|
||||
Result result = Result::ErrorOutOfMemory;
|
||||
|
||||
// Get the bucket base address....
|
||||
Entry* pGroup = this->InitAndFindBucket(key);
|
||||
|
||||
*pExisted = false;
|
||||
*ppValue = nullptr;
|
||||
|
||||
Entry* pMatchingEntry = nullptr;
|
||||
|
||||
while (pGroup != nullptr)
|
||||
{
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pGroup);
|
||||
|
||||
// Search this entry group.
|
||||
uint32 i = 0;
|
||||
for (; i < numEntries; i++)
|
||||
{
|
||||
if (this->m_equalFunc(pGroup[i].key, key))
|
||||
{
|
||||
// We've found the entry.
|
||||
pMatchingEntry = &(pGroup[i]);
|
||||
*pExisted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We've reached the end of the allocated buckets and the entry was not found.
|
||||
// Allocate this entry for the key.
|
||||
if ((pMatchingEntry == nullptr) && (i < Base::EntriesInGroup))
|
||||
{
|
||||
pGroup[i].key = key;
|
||||
pMatchingEntry = &(pGroup[i]);
|
||||
this->m_numEntries++;
|
||||
this->SetGroupFooterNumEntries(pGroup, numEntries + 1);
|
||||
}
|
||||
|
||||
if (pMatchingEntry != nullptr)
|
||||
{
|
||||
*ppValue = &(pMatchingEntry->value);
|
||||
result = Result::Success;
|
||||
break;
|
||||
}
|
||||
|
||||
// Chain to the next entry group.
|
||||
pGroup = this->AllocateNextGroup(pGroup);
|
||||
}
|
||||
|
||||
PAL_ASSERT(result == Result::Success);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Gets a pointer to the value that matches the key. Returns null if no entry is present matching the specified key.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Value* HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::FindKey(
|
||||
const Key& key
|
||||
) const
|
||||
{
|
||||
// Get the bucket base address.
|
||||
Entry* pGroup = this->FindBucket(key);
|
||||
Entry* pMatchingEntry = nullptr;
|
||||
|
||||
while (pGroup != nullptr)
|
||||
{
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pGroup);
|
||||
|
||||
// Search this entry group
|
||||
uint32 i = 0;
|
||||
for (; i < numEntries; i++)
|
||||
{
|
||||
if (this->m_equalFunc(pGroup[i].key, key))
|
||||
{
|
||||
// We've found the entry.
|
||||
pMatchingEntry = &(pGroup[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pMatchingEntry != nullptr) || (i < Base::EntriesInGroup))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Chain to the next entry group.
|
||||
pGroup = this->GetNextGroup(pGroup);
|
||||
}
|
||||
|
||||
return (pMatchingEntry != nullptr) ? &(pMatchingEntry->value) : nullptr;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Inserts a key/value pair entry if it doesn't already exist.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Insert(
|
||||
const Key& key,
|
||||
const Value& value)
|
||||
{
|
||||
bool existed = true;
|
||||
Value* pValue = nullptr;
|
||||
|
||||
Result result = FindAllocate(key, &existed, &pValue);
|
||||
|
||||
// Add the new value if it did not exist already. If FindAllocate returns Success, pValue != nullptr.
|
||||
if ((result == Result::Success) && (existed == false))
|
||||
{
|
||||
*pValue = value;
|
||||
}
|
||||
|
||||
PAL_ASSERT(result == Result::Success);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Removes an entry with the specified key.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
bool HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Erase(
|
||||
const Key& key)
|
||||
{
|
||||
// Get the bucket base address.
|
||||
Entry* pGroup = this->FindBucket(key);
|
||||
|
||||
Entry* pFoundEntry = nullptr;
|
||||
Entry* pLastEntry = nullptr;
|
||||
Entry* pLastEntryGroup = nullptr;
|
||||
|
||||
// Find the entry to delete
|
||||
while (pGroup != nullptr)
|
||||
{
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pGroup);
|
||||
|
||||
// Search each group
|
||||
uint32 i = 0;
|
||||
for (; i < numEntries; i++)
|
||||
{
|
||||
if (this->m_equalFunc(pGroup[i].key, key) == true)
|
||||
{
|
||||
// We shouldn't find the same key twice.
|
||||
PAL_ASSERT(pFoundEntry == nullptr);
|
||||
|
||||
pFoundEntry = &(pGroup[i]);
|
||||
}
|
||||
|
||||
// keep track of last entry of all groups in bucket
|
||||
pLastEntry = &(pGroup[i]);
|
||||
pLastEntryGroup = pGroup;
|
||||
}
|
||||
|
||||
// Chain to the next entry group.
|
||||
pGroup = this->GetNextGroup(pGroup);
|
||||
}
|
||||
|
||||
// Copy the last entry's data into the entry that we are removing and invalidate the last entry as it now appears
|
||||
// earlier in the list. This also handles the case where the entry to be removed is the last entry.
|
||||
if (pFoundEntry != nullptr)
|
||||
{
|
||||
PAL_ASSERT(pLastEntry != nullptr);
|
||||
|
||||
pFoundEntry->key = pLastEntry->key;
|
||||
pFoundEntry->value = pLastEntry->value;
|
||||
|
||||
memset(pLastEntry, 0, sizeof(Entry));
|
||||
|
||||
PAL_ASSERT(this->m_numEntries > 0);
|
||||
this->m_numEntries--;
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pLastEntryGroup);
|
||||
this->SetGroupFooterNumEntries(pLastEntryGroup, numEntries - 1);
|
||||
}
|
||||
|
||||
return (pFoundEntry != nullptr);
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashMapImpl.h
|
||||
* @brief PAL utility collection HashMap class implementation.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBaseImpl.h"
|
||||
#include "palHashMap.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// =====================================================================================================================
|
||||
// Gets a pointer to the value that matches the key. If the key is not present, a pointer to empty space for the value
|
||||
// is returned.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::FindAllocate(
|
||||
const Key& key, // Key to search for.
|
||||
bool* pExisted, // [out] True if a matching key was found.
|
||||
Value** ppValue) // [out] Pointer to the value entry of the hash map's entry for the specified key.
|
||||
{
|
||||
PAL_ASSERT(pExisted != nullptr);
|
||||
PAL_ASSERT(ppValue != nullptr);
|
||||
|
||||
Entry* pEntry = nullptr;
|
||||
Result result = Base::FindAllocateEntry(key, pExisted, &pEntry);
|
||||
if (result == Result::Success)
|
||||
{
|
||||
*ppValue = &pEntry->value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Gets a pointer to the value that matches the key. Returns null if no entry is present matching the specified key.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Value* HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::FindKey(
|
||||
const Key& key
|
||||
) const
|
||||
{
|
||||
Entry* pEntry = Base::FindEntry(key);
|
||||
return (pEntry != nullptr) ? &pEntry->value : nullptr;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Inserts a key/value pair entry if it doesn't already exist.
|
||||
template<typename Key,
|
||||
typename Value,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashMap<Key, Value, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Insert(
|
||||
const Key& key,
|
||||
const Value& value)
|
||||
{
|
||||
bool existed = true;
|
||||
Entry* pEntry = nullptr;
|
||||
|
||||
Result result = Base::FindAllocateEntry(key, &existed, &pEntry);
|
||||
|
||||
// Add the new value if it did not exist already. If FindAllocate returns Success, pValue != nullptr.
|
||||
if ((result == Result::Success) && (existed == false))
|
||||
{
|
||||
pEntry->value = value;
|
||||
}
|
||||
|
||||
PAL_ASSERT(result == Result::Success);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,144 +1,131 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashSet.h
|
||||
* @brief PAL utility collection HashSet class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBase.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Encapsulates one entry of a hash set.
|
||||
template<typename Key>
|
||||
struct HashSetEntry
|
||||
{
|
||||
Key key; ///< Hash set entry key.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Templated hash set container.
|
||||
*
|
||||
* This is meant for storing elements of an arbitrary (but uniform) key type. Supported operations:
|
||||
*
|
||||
* - Searching
|
||||
* - Insertion
|
||||
* - Deletion
|
||||
* - Iteration
|
||||
*
|
||||
* HashFunc is a functor for hashing keys. Built-in choices for HashFunc are:
|
||||
*
|
||||
* - DefaultHashFunc: Good choice when the key is a pointer.
|
||||
* - JenkinsHashFunc: Good choice when the key is arbitrary binary data.
|
||||
* - StringJenkinsHashFunc: Good choice when the key is a C-style string.
|
||||
*
|
||||
* EqualFunc is a functor for comparing keys. Built-in choices for EqualFunc are:
|
||||
*
|
||||
* - DefaultEqualFunc: Determines keys are equal by bitwise comparison.
|
||||
* - StringEqualFunc: Treats keys as a char* and compares them as C-style strings.
|
||||
*
|
||||
* @warning This class is not thread-safe for Insert, Erase, or iteration!
|
||||
* @warning Init() must be called before using this container. Begin() and Reset() can be safely called before
|
||||
* initialization and Begin() will always return an iterator that points to null.
|
||||
*
|
||||
* For more details please refer to @ref HashBase.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc = DefaultHashFunc,
|
||||
template<typename> class EqualFunc = DefaultEqualFunc,
|
||||
typename AllocFunc = HashAllocator<Allocator>,
|
||||
size_t GroupSize = PAL_CACHE_LINE_BYTES * 2>
|
||||
class HashSet : public HashBase<Key,
|
||||
HashSetEntry<Key>,
|
||||
Allocator,
|
||||
HashFunc<Key>,
|
||||
EqualFunc<Key>,
|
||||
AllocFunc,
|
||||
GroupSize>
|
||||
{
|
||||
public:
|
||||
/// Convenience typedef for a templated entry of this hash set.
|
||||
typedef HashSetEntry<Key> Entry;
|
||||
|
||||
/// @internal Constructor
|
||||
///
|
||||
/// @param [in] numBuckets Number of buckets to allocate for this hash container. The initial hash container will
|
||||
/// take (buckets * GroupSize) bytes.
|
||||
/// @param [in] pAllocator Pointer to an allocator that will create system memory requested by this hash container.
|
||||
explicit HashSet(uint32 numBuckets, Allocator*const pAllocator) : Base::HashBase(numBuckets, pAllocator) {}
|
||||
virtual ~HashSet() { }
|
||||
|
||||
/// Finds a given entry; if no entry was found, allocate it.
|
||||
///
|
||||
/// @param [in] ppKey Key to search for.
|
||||
/// @param [out] pExisted True if an entry for the specified key existed before this call was made.
|
||||
/// False indicates that a new entry was allocated as a result of this call.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully
|
||||
/// @ref ErrorOutOfMemory if the operation failed because an internal memory allocation failed.
|
||||
Result FindAllocate(Key** ppKey, bool* pExisted);
|
||||
|
||||
/// Returns true if the specified key exists in the set.
|
||||
///
|
||||
/// @param [in] key Key to search for.
|
||||
///
|
||||
/// @returns True if the specified key exists in the set.
|
||||
bool Contains(const Key& key) const;
|
||||
|
||||
/// Inserts an entry.
|
||||
///
|
||||
/// No action will be taken if an entry matching this key already exists in the set.
|
||||
///
|
||||
/// @param [in] key New entry to insert.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully, or @ref ErrorOutOfMemory if the operation failed
|
||||
/// because an internal memory allocation failed.
|
||||
Result Insert(const Key& key);
|
||||
|
||||
/// Removes an entry that matches the specified key.
|
||||
///
|
||||
/// @param [in] key Key of the entry to erase.
|
||||
///
|
||||
/// @returns True if the erase completed successfully, false if an entry for this key did not exist.
|
||||
bool Erase(const Key& key);
|
||||
|
||||
private:
|
||||
// Typedef for the specialized 'HashBase' object we're inheriting from so we can use properly qualified names when
|
||||
// accessing members of HashBase.
|
||||
typedef HashBase<Key, HashSetEntry<Key>, Allocator, HashFunc<Key>, EqualFunc<Key>, AllocFunc, GroupSize> Base;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(HashSet);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(HashSet);
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashSet.h
|
||||
* @brief PAL utility collection HashSet class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBase.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Encapsulates one entry of a hash set.
|
||||
template<typename Key>
|
||||
struct HashSetEntry
|
||||
{
|
||||
Key key; ///< Hash set entry key.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Templated hash set container.
|
||||
*
|
||||
* This is meant for storing elements of an arbitrary (but uniform) key type. Supported operations:
|
||||
*
|
||||
* - Searching
|
||||
* - Insertion
|
||||
* - Deletion
|
||||
* - Iteration
|
||||
*
|
||||
* HashFunc is a functor for hashing keys. Built-in choices for HashFunc are:
|
||||
*
|
||||
* - DefaultHashFunc: Default hash function, selects best hash function based on type of key.
|
||||
* - PointerHashFunc: Good choice when the key is a pointer.
|
||||
* - JenkinsHashFunc: Good choice when the key is arbitrary binary data.
|
||||
* - StringJenkinsHashFunc: Good choice when the key is a C-style string.
|
||||
*
|
||||
* EqualFunc is a functor for comparing keys. Built-in choices for EqualFunc are:
|
||||
*
|
||||
* - DefaultEqualFunc: Determines keys are equal by bitwise comparison.
|
||||
* - StringEqualFunc: Treats keys as a char* and compares them as C-style strings.
|
||||
*
|
||||
* @warning This class is not thread-safe for Insert, Erase, or iteration!
|
||||
* @warning Init() must be called before using this container. Begin() and Reset() can be safely called before
|
||||
* initialization and Begin() will always return an iterator that points to null.
|
||||
*
|
||||
* For more details please refer to @ref HashBase.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc = DefaultHashFunc,
|
||||
template<typename> class EqualFunc = DefaultEqualFunc,
|
||||
typename AllocFunc = HashAllocator<Allocator>,
|
||||
size_t GroupSize = PAL_CACHE_LINE_BYTES * 2>
|
||||
class HashSet : public HashBase<Key,
|
||||
HashSetEntry<Key>,
|
||||
Allocator,
|
||||
HashFunc<Key>,
|
||||
EqualFunc<Key>,
|
||||
AllocFunc,
|
||||
GroupSize>
|
||||
{
|
||||
public:
|
||||
/// Convenience typedef for a templated entry of this hash set.
|
||||
typedef HashSetEntry<Key> Entry;
|
||||
|
||||
/// @internal Constructor
|
||||
///
|
||||
/// @param [in] numBuckets Number of buckets to allocate for this hash container. The initial hash container will
|
||||
/// take (buckets * GroupSize) bytes.
|
||||
/// @param [in] pAllocator Pointer to an allocator that will create system memory requested by this hash container.
|
||||
explicit HashSet(uint32 numBuckets, Allocator*const pAllocator) : Base::HashBase(numBuckets, pAllocator) {}
|
||||
~HashSet() { }
|
||||
|
||||
/// Finds a given entry; if no entry was found, allocate it.
|
||||
///
|
||||
/// @param [in] ppKey Key to search for.
|
||||
/// @param [out] pExisted True if an entry for the specified key existed before this call was made.
|
||||
/// False indicates that a new entry was allocated as a result of this call.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully
|
||||
/// @ref ErrorOutOfMemory if the operation failed because an internal memory allocation failed.
|
||||
Result FindAllocate(Key** ppKey, bool* pExisted);
|
||||
|
||||
/// Inserts an entry.
|
||||
///
|
||||
/// No action will be taken if an entry matching this key already exists in the set.
|
||||
///
|
||||
/// @param [in] key New entry to insert.
|
||||
///
|
||||
/// @returns @ref Success if the operation completed successfully, or @ref ErrorOutOfMemory if the operation failed
|
||||
/// because an internal memory allocation failed.
|
||||
Result Insert(const Key& key);
|
||||
|
||||
private:
|
||||
// Typedef for the specialized 'HashBase' object we're inheriting from so we can use properly qualified names when
|
||||
// accessing members of HashBase.
|
||||
typedef HashBase<Key, HashSetEntry<Key>, Allocator, HashFunc<Key>, EqualFunc<Key>, AllocFunc, GroupSize> Base;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(HashSet);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(HashSet);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,231 +1,75 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashSetImpl.h
|
||||
* @brief PAL utility collection HashSet class implementation.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBaseImpl.h"
|
||||
#include "palHashSet.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// =====================================================================================================================
|
||||
// Inserts a key if it doesn't already exist.
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashSet<Key, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Insert(
|
||||
const Key& key)
|
||||
{
|
||||
Key* pKey = const_cast<Key*>(&key);
|
||||
bool existed;
|
||||
const Result result = FindAllocate(&pKey, &existed);
|
||||
if (existed == false)
|
||||
{
|
||||
*pKey = key;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Finds a given entry; if no entry was found, allocate it.
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashSet<Key, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::FindAllocate(
|
||||
Key** ppKey,
|
||||
bool* pExisted)
|
||||
{
|
||||
PAL_ASSERT(ppKey != nullptr);
|
||||
PAL_ASSERT(pExisted != nullptr);
|
||||
|
||||
Result result = Result::ErrorOutOfMemory;
|
||||
|
||||
// Get the bucket base address.
|
||||
Entry* pGroup = this->InitAndFindBucket(**ppKey);
|
||||
|
||||
Entry* pMatchingEntry = nullptr;
|
||||
|
||||
while (pGroup != nullptr)
|
||||
{
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pGroup);
|
||||
// Search this entry group.
|
||||
uint32 i = 0;
|
||||
for (; i < numEntries; i++)
|
||||
{
|
||||
if (this->m_equalFunc(pGroup[i].key, **ppKey))
|
||||
{
|
||||
// We've found the entry.
|
||||
pMatchingEntry = &(pGroup[i]);
|
||||
*pExisted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pMatchingEntry == nullptr) && (i < Base::EntriesInGroup))
|
||||
{
|
||||
// We've reached the end of the bucket and the entry was not found. Allocate this entry for the key.
|
||||
*pExisted = false;
|
||||
*ppKey = &pGroup[i].key;
|
||||
pMatchingEntry = &(pGroup[i]);
|
||||
this->m_numEntries++;
|
||||
this->SetGroupFooterNumEntries(pGroup, numEntries + 1);
|
||||
}
|
||||
|
||||
if (pMatchingEntry != nullptr)
|
||||
{
|
||||
result = Result::Success;
|
||||
break;
|
||||
}
|
||||
|
||||
// Chain to the next entry group.
|
||||
pGroup = this->AllocateNextGroup(pGroup);
|
||||
}
|
||||
|
||||
PAL_ASSERT(result == Result::Success);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Searches for the specified key to see if it exists.
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
bool HashSet<Key, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Contains(
|
||||
const Key& key
|
||||
) const
|
||||
{
|
||||
// Get the bucket base address.
|
||||
Entry* pGroup = this->FindBucket(key);
|
||||
Entry* pMatchingEntry = nullptr;
|
||||
|
||||
while (pGroup != nullptr)
|
||||
{
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pGroup);
|
||||
|
||||
// Search this entry group.
|
||||
uint32 i = 0;
|
||||
for (; i < numEntries; i++)
|
||||
{
|
||||
if (this->m_equalFunc(pGroup[i].key, key))
|
||||
{
|
||||
// We've found the entry.
|
||||
pMatchingEntry = &(pGroup[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pMatchingEntry != nullptr) || (i < Base::EntriesInGroup))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Chain to the next entry group.
|
||||
pGroup = this->GetNextGroup(pGroup);
|
||||
}
|
||||
|
||||
return (pMatchingEntry != nullptr);
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Removes an entry with the specified key.
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
bool HashSet<Key, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Erase(
|
||||
const Key& key)
|
||||
{
|
||||
// Get the bucket base address.
|
||||
Entry* pGroup = this->FindBucket(key);
|
||||
|
||||
Entry* pFoundEntry = nullptr;
|
||||
Entry* pLastEntry = nullptr;
|
||||
|
||||
Entry* pLastEntryGroup = nullptr;
|
||||
|
||||
// Find the entry to delete.
|
||||
while ((pGroup != nullptr))
|
||||
{
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pGroup);
|
||||
|
||||
// Search this entry
|
||||
uint32 i = 0;
|
||||
for (; i < numEntries; i++)
|
||||
{
|
||||
if (this->m_equalFunc(pGroup[i].key, key) == true)
|
||||
{
|
||||
// We shouldn't find the same key twice.
|
||||
PAL_ASSERT(pFoundEntry == nullptr);
|
||||
|
||||
pFoundEntry = &(pGroup[i]);
|
||||
}
|
||||
|
||||
// keep track of last entry of all groups in bucket
|
||||
pLastEntry = &(pGroup[i]);
|
||||
pLastEntryGroup = pGroup;
|
||||
}
|
||||
|
||||
// Chain to the next entry group
|
||||
pGroup = this->GetNextGroup(pGroup);
|
||||
}
|
||||
|
||||
// Copy the last entry's data into the entry that we are removing and invalidate the last entry as it now appears
|
||||
// earlier in the list. This also handles the case where the entry to be removed is the last entry.
|
||||
if (pFoundEntry != nullptr)
|
||||
{
|
||||
PAL_ASSERT(pLastEntry != nullptr);
|
||||
|
||||
pFoundEntry->key = pLastEntry->key;
|
||||
memset(pLastEntry, 0, sizeof(Entry));
|
||||
|
||||
PAL_ASSERT(this->m_numEntries > 0);
|
||||
this->m_numEntries--;
|
||||
const uint32 numEntries = this->GetGroupFooterNumEntries(pLastEntryGroup);
|
||||
this->SetGroupFooterNumEntries(pLastEntryGroup, numEntries - 1);
|
||||
}
|
||||
|
||||
return (pFoundEntry != nullptr);
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palHashSetImpl.h
|
||||
* @brief PAL utility collection HashSet class implementation.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palHashBaseImpl.h"
|
||||
#include "palHashSet.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// =====================================================================================================================
|
||||
// Inserts a key if it doesn't already exist.
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashSet<Key, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::Insert(
|
||||
const Key& key)
|
||||
{
|
||||
Entry* pEntry = nullptr;
|
||||
bool existed = false;
|
||||
return Base::FindAllocateEntry(key, &existed, &pEntry);
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Finds a given entry; if no entry was found, allocate it.
|
||||
template<typename Key,
|
||||
typename Allocator,
|
||||
template<typename> class HashFunc,
|
||||
template<typename> class EqualFunc,
|
||||
typename AllocFunc,
|
||||
size_t GroupSize>
|
||||
Result HashSet<Key, Allocator, HashFunc, EqualFunc, AllocFunc, GroupSize>::FindAllocate(
|
||||
Key** ppKey,
|
||||
bool* pExisted)
|
||||
{
|
||||
PAL_ASSERT(ppKey != nullptr);
|
||||
PAL_ASSERT(pExisted != nullptr);
|
||||
|
||||
static_assert(offsetof(Entry, key) == 0);
|
||||
return Base::FindAllocateEntry(**ppKey, pExisted, reinterpret_cast<Entry**>(ppKey));
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,293 +1,293 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palIntrusiveList.h
|
||||
* @brief PAL utility collection IntrusiveList and IntrusiveListIterator class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palAssert.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
template<typename T> class IntrusiveList;
|
||||
template<typename T> class IntrusiveListIterator;
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Encapsulates one node of an intrusive double-linked-list.
|
||||
*
|
||||
* A node is associated with one data pointer at construction. The data pointer cannot be changed and must be non-null.
|
||||
*
|
||||
* Note that InList() allows intrusive list users to verify if a given value has been stored in a list without iterating
|
||||
* over the list provided that each node object has been designated for a particular list.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class IntrusiveListNode
|
||||
{
|
||||
public:
|
||||
/// @param [in,out] pData Address of the data element which contains this intrusive node.
|
||||
explicit IntrusiveListNode(T* pData);
|
||||
|
||||
/// Returns true if this node is present in an intrusive list.
|
||||
bool InList() const;
|
||||
|
||||
private:
|
||||
// This special constructor is provided for IntrusiveList's sentinel node which must have a null data pointer.
|
||||
IntrusiveListNode();
|
||||
|
||||
T*const m_pData; // The data object that contains this node.
|
||||
IntrusiveListNode<T>* m_pPrev; // Previous node in the list or null if this node is not in a list.
|
||||
IntrusiveListNode<T>* m_pNext; // Next node in the list or null if this node is not in a list.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(IntrusiveListNode);
|
||||
|
||||
// Although this is a transgression of coding standards, it prevents IntrusiveListNode from requiring public
|
||||
// accessor functions. The added encapsulation this provides is worthwhile.
|
||||
friend class IntrusiveList<T>;
|
||||
friend class IntrusiveListIterator<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Iterator for traversal of elements in a List collection.
|
||||
*
|
||||
* Allows traversal of all elements in a List going either forwards or backwards.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class IntrusiveListIterator
|
||||
{
|
||||
public:
|
||||
~IntrusiveListIterator() { }
|
||||
|
||||
/// Returns true unless the iterator has advanced past the end of the list.
|
||||
bool IsValid() const { return m_pCurrent != m_pSentinel; }
|
||||
|
||||
/// Returns a pointer to the current element. Returns null if the iterator is invalid.
|
||||
T* Get() const { return m_pCurrent->m_pData; }
|
||||
|
||||
/// Advances the iterator to the previous position (move backward).
|
||||
void Prev() { m_pCurrent = m_pCurrent->m_pPrev; }
|
||||
|
||||
/// Advances the iterator to the next position (move forward).
|
||||
void Next() { m_pCurrent = m_pCurrent->m_pNext; }
|
||||
|
||||
/// Moves the iterator back to the start of the list.
|
||||
void Restart() { m_pCurrent = m_pSentinel->m_pNext; }
|
||||
|
||||
private:
|
||||
IntrusiveListIterator(const IntrusiveListNode<T>*const pSentinel, IntrusiveListNode<T>* pStart);
|
||||
|
||||
const IntrusiveListNode<T>*const m_pSentinel; // We need the sentinel to locate the list boundaries.
|
||||
IntrusiveListNode<T>* m_pCurrent; // Pointer to the current node.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(IntrusiveListIterator);
|
||||
|
||||
// Although this is a transgression of coding standards, it means that List does not need to have a public interface
|
||||
// specifically to implement this class. The added encapsulation this provides is worthwhile.
|
||||
friend class IntrusiveList<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Templated, doubly-linked, intrusive, list container.
|
||||
*
|
||||
* This is meant for storing non-null pointers to elements of an arbitrary type using externally managed nodes.
|
||||
* Operations which this class supports are:
|
||||
*
|
||||
* - Insertion at any point
|
||||
* - Deletion at any point
|
||||
* - Forwards and reverse iteration
|
||||
*
|
||||
* @warning This class is not thread-safe.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class IntrusiveList
|
||||
{
|
||||
public:
|
||||
/// A convenient shorthand for IntrusiveListNode.
|
||||
typedef IntrusiveListNode<T> Node;
|
||||
|
||||
/// A convenient shorthand for IntrusiveListIterator.
|
||||
typedef IntrusiveListIterator<T> Iter;
|
||||
|
||||
IntrusiveList();
|
||||
|
||||
/// Returns the number of elements in the list, not counting the sentinel.
|
||||
size_t NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns true if the list is empty.
|
||||
bool IsEmpty() const { return m_sentinel.m_pNext == &m_sentinel; }
|
||||
|
||||
/// Returns an iterator pointing to the first element in the list. If the list is empty, the iterator starts out
|
||||
/// pointing at the permanent sentinel node.
|
||||
///
|
||||
/// @returns An iterator pointing at the front end of the list.
|
||||
Iter Begin() const { return Iter(&m_sentinel, m_sentinel.m_pNext); }
|
||||
|
||||
/// Returns an iterator pointing to the last element in the list. If the list is empty, the iterator starts out
|
||||
/// pointing at the permanent sentinel node.
|
||||
///
|
||||
/// @returns An iterator pointing at the back end of the list.
|
||||
Iter End() const { return Iter(&m_sentinel, m_sentinel.m_pPrev); }
|
||||
|
||||
/// Returns the data pointer at the front of the list.
|
||||
///
|
||||
/// @returns The data pointer at the front of the list or null if the list is empty.
|
||||
T* Front() const { return m_sentinel.m_pNext->m_pData; }
|
||||
|
||||
/// Returns the data pointer at the back of the list.
|
||||
///
|
||||
/// @returns The data pointer at the back of the list or null if the list is empty.
|
||||
T* Back() const { return m_sentinel.m_pPrev->m_pData; }
|
||||
|
||||
/// Pushes the specified node onto the front of the list.
|
||||
///
|
||||
/// @param [in] pNode Externally-owned list node to link into the list.
|
||||
void PushFront(Node* pNode) { InsertBefore(m_sentinel.m_pNext, pNode); }
|
||||
|
||||
/// Pushes the specified node onto the back of the list.
|
||||
///
|
||||
/// @param [in] pNode Externally-owned list node to link into the list.
|
||||
void PushBack(Node* pNode) { InsertBefore(&m_sentinel, pNode); }
|
||||
|
||||
/// Pushes the contents of pSource onto the front of this list. The ordering of pSource is preserved, meaning that
|
||||
/// the front of pSource will be the new front of this list. Note that pSource will be left entirely empty.
|
||||
///
|
||||
/// It is illegal to call this function with an empty pSource.
|
||||
///
|
||||
/// @param [in] pSource The contents of pSource will be pushed in-order onto the front of this list.
|
||||
void PushFrontList(IntrusiveList<T>* pSource);
|
||||
|
||||
/// Pushes the contents of pSource onto the back of this list. The ordering of pSource is preserved, meaning that
|
||||
/// the end of pSource will be the new end of this list. Note that pSource will be left entirely empty.
|
||||
///
|
||||
/// It is illegal to call this function with an empty pSource.
|
||||
///
|
||||
/// @param [in] pSource The contents of pSource will be pushed in-order onto the back of this list.
|
||||
void PushBackList(IntrusiveList<T>* pSource);
|
||||
|
||||
/// Inserts the specified node before a particular node in a list.
|
||||
///
|
||||
/// If the iterator has advanced off the end of the list (i.e., the iterator is invalid), the added node will be the
|
||||
/// new tail node.
|
||||
///
|
||||
/// @param [in] iter Identifies a node where the insertion should take place. The iterator will point to the same
|
||||
/// spot in the list after insertion.
|
||||
/// @param [in] pNode Externally-owned list node to link into the list.
|
||||
void InsertBefore(const Iter& iter, Node* pNode);
|
||||
|
||||
/// Removes the node at the specified position from the list.
|
||||
///
|
||||
/// It is illegal to call this function with an iterator that has already advanced off the end of the list.
|
||||
///
|
||||
/// @param [in,out] pIter Iterator identifying the node to be removed. After the node is removed, this iterator
|
||||
/// will be advanced to the next node. If this call removes the final remaining node in the
|
||||
/// list then the iterator will point at the sentinel and will be invalid.
|
||||
void Erase(Iter* pIter);
|
||||
|
||||
/// Removes the node at the specified position from the list. It is illegal to call this function with a Node that
|
||||
/// not in this list.
|
||||
///
|
||||
/// @param [in] pNode Node to be removed.
|
||||
void Erase(Node* pNode);
|
||||
|
||||
/// Removes all nodes from the list.
|
||||
void EraseAll();
|
||||
|
||||
/// Truncates the list without touching the elements
|
||||
void InvalidateList()
|
||||
{
|
||||
m_sentinel.m_pNext = &m_sentinel;
|
||||
m_sentinel.m_pPrev = &m_sentinel;
|
||||
m_numElements = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void InsertBefore(Node* pBeforeMe, Node* pNode);
|
||||
void Unlink(Node* pNode);
|
||||
|
||||
Node m_sentinel; // Ties the head to the tail and signifies the boundary of the list.
|
||||
size_t m_numElements; // Number of elements.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(IntrusiveList);
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
// This is the public node constructor; it must be given a non-null data pointer.
|
||||
template<typename T>
|
||||
IntrusiveListNode<T>::IntrusiveListNode(
|
||||
T* pData)
|
||||
:
|
||||
m_pData(pData),
|
||||
m_pPrev(nullptr),
|
||||
m_pNext(nullptr)
|
||||
{
|
||||
PAL_ASSERT(pData != nullptr);
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// This is the private node constructor which is used exclusively for sentinel nodes.
|
||||
template<typename T>
|
||||
IntrusiveListNode<T>::IntrusiveListNode()
|
||||
:
|
||||
m_pData(nullptr),
|
||||
m_pPrev(nullptr),
|
||||
m_pNext(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Returns true if this node is present in an intrusive list.
|
||||
template<typename T>
|
||||
bool IntrusiveListNode<T>::InList() const
|
||||
{
|
||||
// The node pointers should always be null or non-null together.
|
||||
PAL_DEBUG_BUILD_ONLY_ASSERT((m_pPrev == nullptr) == (m_pNext == nullptr));
|
||||
|
||||
return (m_pNext != nullptr);
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T>
|
||||
IntrusiveListIterator<T>::IntrusiveListIterator(
|
||||
const IntrusiveListNode<T>*const pSentinel,
|
||||
IntrusiveListNode<T>* pStart)
|
||||
:
|
||||
m_pSentinel(pSentinel),
|
||||
m_pCurrent(pStart)
|
||||
{
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palIntrusiveList.h
|
||||
* @brief PAL utility collection IntrusiveList and IntrusiveListIterator class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palAssert.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
template<typename T> class IntrusiveList;
|
||||
template<typename T> class IntrusiveListIterator;
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Encapsulates one node of an intrusive double-linked-list.
|
||||
*
|
||||
* A node is associated with one data pointer at construction. The data pointer cannot be changed and must be non-null.
|
||||
*
|
||||
* Note that InList() allows intrusive list users to verify if a given value has been stored in a list without iterating
|
||||
* over the list provided that each node object has been designated for a particular list.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class IntrusiveListNode
|
||||
{
|
||||
public:
|
||||
/// @param [in,out] pData Address of the data element which contains this intrusive node.
|
||||
explicit IntrusiveListNode(T* pData);
|
||||
|
||||
/// Returns true if this node is present in an intrusive list.
|
||||
bool InList() const;
|
||||
|
||||
private:
|
||||
// This special constructor is provided for IntrusiveList's sentinel node which must have a null data pointer.
|
||||
IntrusiveListNode();
|
||||
|
||||
T*const m_pData; // The data object that contains this node.
|
||||
IntrusiveListNode<T>* m_pPrev; // Previous node in the list or null if this node is not in a list.
|
||||
IntrusiveListNode<T>* m_pNext; // Next node in the list or null if this node is not in a list.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(IntrusiveListNode);
|
||||
|
||||
// Although this is a transgression of coding standards, it prevents IntrusiveListNode from requiring public
|
||||
// accessor functions. The added encapsulation this provides is worthwhile.
|
||||
friend class IntrusiveList<T>;
|
||||
friend class IntrusiveListIterator<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Iterator for traversal of elements in a List collection.
|
||||
*
|
||||
* Allows traversal of all elements in a List going either forwards or backwards.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class IntrusiveListIterator
|
||||
{
|
||||
public:
|
||||
~IntrusiveListIterator() { }
|
||||
|
||||
/// Returns true unless the iterator has advanced past the end of the list.
|
||||
bool IsValid() const { return m_pCurrent != m_pSentinel; }
|
||||
|
||||
/// Returns a pointer to the current element. Returns null if the iterator is invalid.
|
||||
T* Get() const { return m_pCurrent->m_pData; }
|
||||
|
||||
/// Advances the iterator to the previous position (move backward).
|
||||
void Prev() { m_pCurrent = m_pCurrent->m_pPrev; }
|
||||
|
||||
/// Advances the iterator to the next position (move forward).
|
||||
void Next() { m_pCurrent = m_pCurrent->m_pNext; }
|
||||
|
||||
/// Moves the iterator back to the start of the list.
|
||||
void Restart() { m_pCurrent = m_pSentinel->m_pNext; }
|
||||
|
||||
private:
|
||||
IntrusiveListIterator(const IntrusiveListNode<T>*const pSentinel, IntrusiveListNode<T>* pStart);
|
||||
|
||||
const IntrusiveListNode<T>*const m_pSentinel; // We need the sentinel to locate the list boundaries.
|
||||
IntrusiveListNode<T>* m_pCurrent; // Pointer to the current node.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(IntrusiveListIterator);
|
||||
|
||||
// Although this is a transgression of coding standards, it means that List does not need to have a public interface
|
||||
// specifically to implement this class. The added encapsulation this provides is worthwhile.
|
||||
friend class IntrusiveList<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Templated, doubly-linked, intrusive, list container.
|
||||
*
|
||||
* This is meant for storing non-null pointers to elements of an arbitrary type using externally managed nodes.
|
||||
* Operations which this class supports are:
|
||||
*
|
||||
* - Insertion at any point
|
||||
* - Deletion at any point
|
||||
* - Forwards and reverse iteration
|
||||
*
|
||||
* @warning This class is not thread-safe.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class IntrusiveList
|
||||
{
|
||||
public:
|
||||
/// A convenient shorthand for IntrusiveListNode.
|
||||
typedef IntrusiveListNode<T> Node;
|
||||
|
||||
/// A convenient shorthand for IntrusiveListIterator.
|
||||
typedef IntrusiveListIterator<T> Iter;
|
||||
|
||||
IntrusiveList();
|
||||
|
||||
/// Returns the number of elements in the list, not counting the sentinel.
|
||||
size_t NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns true if the list is empty.
|
||||
bool IsEmpty() const { return m_sentinel.m_pNext == &m_sentinel; }
|
||||
|
||||
/// Returns an iterator pointing to the first element in the list. If the list is empty, the iterator starts out
|
||||
/// pointing at the permanent sentinel node.
|
||||
///
|
||||
/// @returns An iterator pointing at the front end of the list.
|
||||
Iter Begin() const { return Iter(&m_sentinel, m_sentinel.m_pNext); }
|
||||
|
||||
/// Returns an iterator pointing to the last element in the list. If the list is empty, the iterator starts out
|
||||
/// pointing at the permanent sentinel node.
|
||||
///
|
||||
/// @returns An iterator pointing at the back end of the list.
|
||||
Iter End() const { return Iter(&m_sentinel, m_sentinel.m_pPrev); }
|
||||
|
||||
/// Returns the data pointer at the front of the list.
|
||||
///
|
||||
/// @returns The data pointer at the front of the list or null if the list is empty.
|
||||
T* Front() const { return m_sentinel.m_pNext->m_pData; }
|
||||
|
||||
/// Returns the data pointer at the back of the list.
|
||||
///
|
||||
/// @returns The data pointer at the back of the list or null if the list is empty.
|
||||
T* Back() const { return m_sentinel.m_pPrev->m_pData; }
|
||||
|
||||
/// Pushes the specified node onto the front of the list.
|
||||
///
|
||||
/// @param [in] pNode Externally-owned list node to link into the list.
|
||||
void PushFront(Node* pNode) { InsertBefore(m_sentinel.m_pNext, pNode); }
|
||||
|
||||
/// Pushes the specified node onto the back of the list.
|
||||
///
|
||||
/// @param [in] pNode Externally-owned list node to link into the list.
|
||||
void PushBack(Node* pNode) { InsertBefore(&m_sentinel, pNode); }
|
||||
|
||||
/// Pushes the contents of pSource onto the front of this list. The ordering of pSource is preserved, meaning that
|
||||
/// the front of pSource will be the new front of this list. Note that pSource will be left entirely empty.
|
||||
///
|
||||
/// It is illegal to call this function with an empty pSource.
|
||||
///
|
||||
/// @param [in] pSource The contents of pSource will be pushed in-order onto the front of this list.
|
||||
void PushFrontList(IntrusiveList<T>* pSource);
|
||||
|
||||
/// Pushes the contents of pSource onto the back of this list. The ordering of pSource is preserved, meaning that
|
||||
/// the end of pSource will be the new end of this list. Note that pSource will be left entirely empty.
|
||||
///
|
||||
/// It is illegal to call this function with an empty pSource.
|
||||
///
|
||||
/// @param [in] pSource The contents of pSource will be pushed in-order onto the back of this list.
|
||||
void PushBackList(IntrusiveList<T>* pSource);
|
||||
|
||||
/// Inserts the specified node before a particular node in a list.
|
||||
///
|
||||
/// If the iterator has advanced off the end of the list (i.e., the iterator is invalid), the added node will be the
|
||||
/// new tail node.
|
||||
///
|
||||
/// @param [in] iter Identifies a node where the insertion should take place. The iterator will point to the same
|
||||
/// spot in the list after insertion.
|
||||
/// @param [in] pNode Externally-owned list node to link into the list.
|
||||
void InsertBefore(const Iter& iter, Node* pNode);
|
||||
|
||||
/// Removes the node at the specified position from the list.
|
||||
///
|
||||
/// It is illegal to call this function with an iterator that has already advanced off the end of the list.
|
||||
///
|
||||
/// @param [in,out] pIter Iterator identifying the node to be removed. After the node is removed, this iterator
|
||||
/// will be advanced to the next node. If this call removes the final remaining node in the
|
||||
/// list then the iterator will point at the sentinel and will be invalid.
|
||||
void Erase(Iter* pIter);
|
||||
|
||||
/// Removes the node at the specified position from the list. It is illegal to call this function with a Node that
|
||||
/// not in this list.
|
||||
///
|
||||
/// @param [in] pNode Node to be removed.
|
||||
void Erase(Node* pNode);
|
||||
|
||||
/// Removes all nodes from the list.
|
||||
void EraseAll();
|
||||
|
||||
/// Truncates the list without touching the elements
|
||||
void InvalidateList()
|
||||
{
|
||||
m_sentinel.m_pNext = &m_sentinel;
|
||||
m_sentinel.m_pPrev = &m_sentinel;
|
||||
m_numElements = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void InsertBefore(Node* pBeforeMe, Node* pNode);
|
||||
void Unlink(Node* pNode);
|
||||
|
||||
Node m_sentinel; // Ties the head to the tail and signifies the boundary of the list.
|
||||
size_t m_numElements; // Number of elements.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(IntrusiveList);
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
// This is the public node constructor; it must be given a non-null data pointer.
|
||||
template<typename T>
|
||||
IntrusiveListNode<T>::IntrusiveListNode(
|
||||
T* pData)
|
||||
:
|
||||
m_pData(pData),
|
||||
m_pPrev(nullptr),
|
||||
m_pNext(nullptr)
|
||||
{
|
||||
PAL_ASSERT(pData != nullptr);
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// This is the private node constructor which is used exclusively for sentinel nodes.
|
||||
template<typename T>
|
||||
IntrusiveListNode<T>::IntrusiveListNode()
|
||||
:
|
||||
m_pData(nullptr),
|
||||
m_pPrev(nullptr),
|
||||
m_pNext(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Returns true if this node is present in an intrusive list.
|
||||
template<typename T>
|
||||
bool IntrusiveListNode<T>::InList() const
|
||||
{
|
||||
// The node pointers should always be null or non-null together.
|
||||
PAL_DEBUG_BUILD_ONLY_ASSERT((m_pPrev == nullptr) == (m_pNext == nullptr));
|
||||
|
||||
return (m_pNext != nullptr);
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T>
|
||||
IntrusiveListIterator<T>::IntrusiveListIterator(
|
||||
const IntrusiveListNode<T>*const pSentinel,
|
||||
IntrusiveListNode<T>* pStart)
|
||||
:
|
||||
m_pSentinel(pSentinel),
|
||||
m_pCurrent(pStart)
|
||||
{
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,346 +1,346 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palLinearAllocator.h
|
||||
* @brief * @brief PAL utility allocator LinearAllocator class.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palIntrusiveList.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A linear allocator that allocates virtual memory.
|
||||
*
|
||||
* To improve performance, a linear allocator can be used in performance-critical areas to avoid unnecessary heap
|
||||
* allocations. The VirtualLinearAllocator will instead reserve a specified amount of virtual address space and will
|
||||
* incrementally back it with real memory as necessary.
|
||||
*
|
||||
* As clients reach a steady state, allocations from this allocator will become "free," essentially just costing a
|
||||
* pointer increment.
|
||||
*
|
||||
* This allocator can be used with any of the memory management macros. @see Allocators for more information about the
|
||||
* Allocation pattern.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class VirtualLinearAllocator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] size Maximum size, in bytes, of virtual memory that this allocator should reserve.
|
||||
/// Does not need to be aligned to page size.
|
||||
VirtualLinearAllocator(size_t size) :
|
||||
m_pStart(nullptr),
|
||||
m_pCurrent(nullptr),
|
||||
m_size(size),
|
||||
m_pageSize(0) {}
|
||||
|
||||
/// Destructor.
|
||||
virtual ~VirtualLinearAllocator()
|
||||
{
|
||||
if (m_pStart != nullptr)
|
||||
{
|
||||
// Free all of the pages.
|
||||
Result result = VirtualRelease(m_pStart, m_size);
|
||||
PAL_ASSERT(result == Result::_Success);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the linear allocator by reserving the requested number of pages.
|
||||
///
|
||||
/// @returns Result::Success if memory reservation and committing of the first page is successful.
|
||||
Result Init()
|
||||
{
|
||||
m_pageSize = VirtualPageSize();
|
||||
m_size = Pow2Align(m_size, m_pageSize);
|
||||
|
||||
Result result = VirtualReserve(m_size, &m_pStart);
|
||||
|
||||
if (result == Result::_Success)
|
||||
{
|
||||
result = VirtualCommit(m_pStart, m_pageSize);
|
||||
}
|
||||
|
||||
if (result == Result::_Success)
|
||||
{
|
||||
m_pCurrent = m_pStart;
|
||||
m_pCommittedToPage = VoidPtrInc(m_pCurrent, m_pageSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Allocates a block of memory.
|
||||
///
|
||||
/// @param [in] allocInfo Contains information about the requested allocation.
|
||||
///
|
||||
/// @returns Pointer to the allocated memory, nullptr if the allocation failed.
|
||||
void* Alloc(const AllocInfo& allocInfo)
|
||||
{
|
||||
void* pAlignedCurrent = VoidPtrAlign(m_pCurrent, allocInfo.alignment);
|
||||
void* pNextCurrent = VoidPtrInc(pAlignedCurrent, allocInfo.bytes);
|
||||
void* pAlignedEnd = VoidPtrAlign(pNextCurrent, m_pageSize);
|
||||
|
||||
if (allocInfo.bytes > Remaining())
|
||||
{
|
||||
pAlignedCurrent = nullptr;
|
||||
}
|
||||
else if (pAlignedEnd > m_pCommittedToPage)
|
||||
{
|
||||
const size_t commitBytes = VoidPtrDiff(pAlignedEnd, m_pCommittedToPage);
|
||||
|
||||
const Result result = VirtualCommit(m_pCommittedToPage, commitBytes);
|
||||
|
||||
if (result == Result::_Success)
|
||||
{
|
||||
m_pCommittedToPage = VoidPtrInc(m_pCommittedToPage, commitBytes);
|
||||
m_pCurrent = pNextCurrent;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return nullptr if allocation fails.
|
||||
pAlignedCurrent = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pCurrent = pNextCurrent;
|
||||
}
|
||||
|
||||
return pAlignedCurrent;
|
||||
}
|
||||
|
||||
/// Frees a block of memory.
|
||||
///
|
||||
/// @param [in] freeInfo Contains information about the requested free.
|
||||
void Free(const FreeInfo& freeInfo) {}
|
||||
|
||||
/// Rewinds the current pointer to the specified location to reuse already allocated memory.
|
||||
///
|
||||
/// @param pStart Where to reset the m_pCurrent to.
|
||||
/// @param decommit If true, pages that are rewound are freed/decommitted.
|
||||
void Rewind(void* pStart, bool decommit)
|
||||
{
|
||||
PAL_ASSERT((m_pStart <= pStart) && (pStart <= m_pCurrent));
|
||||
|
||||
if (pStart != m_pCurrent)
|
||||
{
|
||||
if (decommit)
|
||||
{
|
||||
void* pStartPage = VoidPtrAlign(VoidPtrInc(pStart, 1), m_pageSize);
|
||||
void* pCurrentPage = VoidPtrAlign(m_pCurrent, m_pageSize);
|
||||
const size_t numPages = VoidPtrDiff(pCurrentPage, pStartPage) / m_pageSize;
|
||||
|
||||
if (numPages > 0)
|
||||
{
|
||||
Result result = VirtualDecommit(pStartPage, m_pageSize * numPages);
|
||||
PAL_ASSERT(result == Result::_Success);
|
||||
|
||||
m_pCommittedToPage = pStartPage;
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
void* pStartPage = VoidPtrAlign(VoidPtrInc(pStart, 1), m_pageSize);
|
||||
void* pCurrentPage = VoidPtrAlign(m_pCurrent, m_pageSize);
|
||||
const size_t numDwords = VoidPtrDiff(pCurrentPage, pStartPage) / sizeof(uint32);
|
||||
uint32* pNewCurrent = static_cast<uint32*>(pStartPage);
|
||||
|
||||
for (size_t dword = 0; dword < numDwords; dword++)
|
||||
{
|
||||
pNewCurrent[dword] = 0xDEADBEEF;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_pCurrent = pStart;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current pointer to backing memory.
|
||||
///
|
||||
/// @returns Current pointer to backing memory.
|
||||
void* Current() { return m_pCurrent; }
|
||||
|
||||
/// Returns the starting pointer to backing memory.
|
||||
///
|
||||
/// @returns Pointer to the start of backing memory.
|
||||
void* Start() { return m_pStart; }
|
||||
|
||||
/// Returns the number of bytes that have been allocated.
|
||||
///
|
||||
/// @returns Number of bytes allocated through this allocator.
|
||||
size_t BytesAllocated() { return VoidPtrDiff(m_pCurrent, m_pStart); }
|
||||
|
||||
/// Compute remaining unallocated space in the allocator; once this space is exhausted allocations will fail.
|
||||
///
|
||||
/// @returns The size of the remaining unallocated space in bytes.
|
||||
size_t Remaining() const { return m_size - VoidPtrDiff(m_pCurrent, m_pStart); }
|
||||
|
||||
private:
|
||||
void* m_pStart; ///< Pointer to where the backing allocation starts.
|
||||
void* m_pCurrent; ///< Pointer to the current position of backing memory.
|
||||
void* m_pCommittedToPage; ///< Pointer to the end of the last committed page.
|
||||
|
||||
size_t m_size; ///< Size of the allocation.
|
||||
size_t m_pageSize; ///< OS' defined page size.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(VirtualLinearAllocator);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(VirtualLinearAllocator);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A "resource acquisition is initialization" (RAII) wrapper for the LinearAllocator classes.
|
||||
*
|
||||
* The RAII paradigm allows critical sections to be automatically acquired during this class' constructor, and
|
||||
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
||||
* this class for stack-allocated objects.
|
||||
*
|
||||
* This object will ensure that anything allocated the object is allocated on the stack and when it goes out of scope
|
||||
* will be properly "rewound" by the allocator. See the below example.
|
||||
*
|
||||
*
|
||||
* {
|
||||
* [Current pointer = 0x10]
|
||||
* LinearAllocatorAuto allocator(pPtrToAllocator);
|
||||
* Allocations occur ...
|
||||
* [Current pointer = 0x80]
|
||||
* }
|
||||
* [Current pointer rewinds = 0x10]
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <class LinearAllocator>
|
||||
class LinearAllocatorAuto
|
||||
{
|
||||
public:
|
||||
/// Tracks the current start pointer.
|
||||
///
|
||||
/// @param pAllocator The allocator to wrap.
|
||||
/// @param decommit Whether to decommit any pages of memory allocated when this goes out of scope.
|
||||
LinearAllocatorAuto(LinearAllocator* pAllocator, bool decommit)
|
||||
:
|
||||
m_pAllocator(pAllocator),
|
||||
#if PAL_MEMTRACK
|
||||
m_memTracker(pAllocator),
|
||||
#endif
|
||||
m_pStart(nullptr),
|
||||
m_decommit(decommit)
|
||||
{
|
||||
PAL_ASSERT(pAllocator != nullptr);
|
||||
m_pStart = m_pAllocator->Current();
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
Result result = m_memTracker.Init();
|
||||
PAL_ASSERT(result == Result::_Success);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Rewinds any allocations made when this goes out of scope.
|
||||
~LinearAllocatorAuto()
|
||||
{
|
||||
m_pAllocator->Rewind(m_pStart, m_decommit);
|
||||
}
|
||||
|
||||
/// Allocates a block of memory.
|
||||
///
|
||||
/// @param [in] allocInfo Contains information about the requested allocation.
|
||||
///
|
||||
/// @returns Pointer to the allocated memory, nullptr if the allocation failed.
|
||||
void* Alloc(const AllocInfo& allocInfo)
|
||||
{
|
||||
void* pMemory = nullptr;
|
||||
#if PAL_MEMTRACK
|
||||
pMemory = m_memTracker.Alloc(allocInfo);
|
||||
#else
|
||||
pMemory = m_pAllocator->Alloc(allocInfo);
|
||||
#endif
|
||||
|
||||
return pMemory;
|
||||
}
|
||||
|
||||
/// Frees a block of memory.
|
||||
///
|
||||
/// @param [in] freeInfo Contains information about the requested free.
|
||||
void Free(const FreeInfo& freeInfo)
|
||||
{
|
||||
#if PAL_MEMTRACK
|
||||
m_memTracker.Free(freeInfo);
|
||||
#else
|
||||
m_pAllocator->Free(freeInfo);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
LinearAllocator*const m_pAllocator; ///< The LinearAllocator which this object wraps.
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
MemTracker<LinearAllocator> m_memTracker; ///< Memory tracker for this LinearAllocatorAuto.
|
||||
#endif
|
||||
|
||||
void* m_pStart; ///< Where the LinearAllocator started when wrapped by this.
|
||||
const bool m_decommit; ///< Whether to decommit any pages of memory allocated on destruction.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(LinearAllocatorAuto);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(LinearAllocatorAuto);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A simple extension of VirtualLinearAllocator that contains an IntrusiveListNode pointing at itself.
|
||||
* This makes it very easy to create and manage IntrusiveLists of VirtualLinearAllocators.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class VirtualLinearAllocatorWithNode : public VirtualLinearAllocator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
VirtualLinearAllocatorWithNode(size_t size) : VirtualLinearAllocator(size), m_node(this) {}
|
||||
|
||||
/// Destructor.
|
||||
virtual ~VirtualLinearAllocatorWithNode() {}
|
||||
|
||||
/// Gets this linear allocator's associated IntrusiveListNode.
|
||||
///
|
||||
/// @returns Pointer to this allocator's associated IntrusiveListNode.
|
||||
IntrusiveListNode<VirtualLinearAllocatorWithNode>* GetNode() { return &m_node; }
|
||||
|
||||
private:
|
||||
IntrusiveListNode<VirtualLinearAllocatorWithNode> m_node;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(VirtualLinearAllocatorWithNode);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(VirtualLinearAllocatorWithNode);
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palLinearAllocator.h
|
||||
* @brief * @brief PAL utility allocator LinearAllocator class.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palIntrusiveList.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A linear allocator that allocates virtual memory.
|
||||
*
|
||||
* To improve performance, a linear allocator can be used in performance-critical areas to avoid unnecessary heap
|
||||
* allocations. The VirtualLinearAllocator will instead reserve a specified amount of virtual address space and will
|
||||
* incrementally back it with real memory as necessary.
|
||||
*
|
||||
* As clients reach a steady state, allocations from this allocator will become "free," essentially just costing a
|
||||
* pointer increment.
|
||||
*
|
||||
* This allocator can be used with any of the memory management macros. @see Allocators for more information about the
|
||||
* Allocation pattern.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class VirtualLinearAllocator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] size Maximum size, in bytes, of virtual memory that this allocator should reserve.
|
||||
/// Does not need to be aligned to page size.
|
||||
VirtualLinearAllocator(size_t size) :
|
||||
m_pStart(nullptr),
|
||||
m_pCurrent(nullptr),
|
||||
m_size(size),
|
||||
m_pageSize(0) {}
|
||||
|
||||
/// Destructor.
|
||||
virtual ~VirtualLinearAllocator()
|
||||
{
|
||||
if (m_pStart != nullptr)
|
||||
{
|
||||
// Free all of the pages.
|
||||
Result result = VirtualRelease(m_pStart, m_size);
|
||||
PAL_ASSERT(result == Result::_Success);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the linear allocator by reserving the requested number of pages.
|
||||
///
|
||||
/// @returns Result::Success if memory reservation and committing of the first page is successful.
|
||||
Result Init()
|
||||
{
|
||||
m_pageSize = VirtualPageSize();
|
||||
m_size = Pow2Align(m_size, m_pageSize);
|
||||
|
||||
Result result = VirtualReserve(m_size, &m_pStart);
|
||||
|
||||
if (result == Result::_Success)
|
||||
{
|
||||
result = VirtualCommit(m_pStart, m_pageSize);
|
||||
}
|
||||
|
||||
if (result == Result::_Success)
|
||||
{
|
||||
m_pCurrent = m_pStart;
|
||||
m_pCommittedToPage = VoidPtrInc(m_pCurrent, m_pageSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Allocates a block of memory.
|
||||
///
|
||||
/// @param [in] allocInfo Contains information about the requested allocation.
|
||||
///
|
||||
/// @returns Pointer to the allocated memory, nullptr if the allocation failed.
|
||||
void* Alloc(const AllocInfo& allocInfo)
|
||||
{
|
||||
void* pAlignedCurrent = VoidPtrAlign(m_pCurrent, allocInfo.alignment);
|
||||
void* pNextCurrent = VoidPtrInc(pAlignedCurrent, allocInfo.bytes);
|
||||
void* pAlignedEnd = VoidPtrAlign(pNextCurrent, m_pageSize);
|
||||
|
||||
if (allocInfo.bytes > Remaining())
|
||||
{
|
||||
pAlignedCurrent = nullptr;
|
||||
}
|
||||
else if (pAlignedEnd > m_pCommittedToPage)
|
||||
{
|
||||
const size_t commitBytes = VoidPtrDiff(pAlignedEnd, m_pCommittedToPage);
|
||||
|
||||
const Result result = VirtualCommit(m_pCommittedToPage, commitBytes);
|
||||
|
||||
if (result == Result::_Success)
|
||||
{
|
||||
m_pCommittedToPage = VoidPtrInc(m_pCommittedToPage, commitBytes);
|
||||
m_pCurrent = pNextCurrent;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return nullptr if allocation fails.
|
||||
pAlignedCurrent = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pCurrent = pNextCurrent;
|
||||
}
|
||||
|
||||
return pAlignedCurrent;
|
||||
}
|
||||
|
||||
/// Frees a block of memory.
|
||||
///
|
||||
/// @param [in] freeInfo Contains information about the requested free.
|
||||
void Free(const FreeInfo& freeInfo) {}
|
||||
|
||||
/// Rewinds the current pointer to the specified location to reuse already allocated memory.
|
||||
///
|
||||
/// @param pStart Where to reset the m_pCurrent to.
|
||||
/// @param decommit If true, pages that are rewound are freed/decommitted.
|
||||
void Rewind(void* pStart, bool decommit)
|
||||
{
|
||||
PAL_ASSERT((m_pStart <= pStart) && (pStart <= m_pCurrent));
|
||||
|
||||
if (pStart != m_pCurrent)
|
||||
{
|
||||
if (decommit)
|
||||
{
|
||||
void* pStartPage = VoidPtrAlign(VoidPtrInc(pStart, 1), m_pageSize);
|
||||
void* pCurrentPage = VoidPtrAlign(m_pCurrent, m_pageSize);
|
||||
const size_t numPages = VoidPtrDiff(pCurrentPage, pStartPage) / m_pageSize;
|
||||
|
||||
if (numPages > 0)
|
||||
{
|
||||
Result result = VirtualDecommit(pStartPage, m_pageSize * numPages);
|
||||
PAL_ASSERT(result == Result::_Success);
|
||||
|
||||
m_pCommittedToPage = pStartPage;
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
void* pStartPage = VoidPtrAlign(VoidPtrInc(pStart, 1), m_pageSize);
|
||||
void* pCurrentPage = VoidPtrAlign(m_pCurrent, m_pageSize);
|
||||
const size_t numDwords = VoidPtrDiff(pCurrentPage, pStartPage) / sizeof(uint32);
|
||||
uint32* pNewCurrent = static_cast<uint32*>(pStartPage);
|
||||
|
||||
for (size_t dword = 0; dword < numDwords; dword++)
|
||||
{
|
||||
pNewCurrent[dword] = 0xDEADBEEF;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_pCurrent = pStart;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current pointer to backing memory.
|
||||
///
|
||||
/// @returns Current pointer to backing memory.
|
||||
void* Current() { return m_pCurrent; }
|
||||
|
||||
/// Returns the starting pointer to backing memory.
|
||||
///
|
||||
/// @returns Pointer to the start of backing memory.
|
||||
void* Start() { return m_pStart; }
|
||||
|
||||
/// Returns the number of bytes that have been allocated.
|
||||
///
|
||||
/// @returns Number of bytes allocated through this allocator.
|
||||
size_t BytesAllocated() { return VoidPtrDiff(m_pCurrent, m_pStart); }
|
||||
|
||||
/// Compute remaining unallocated space in the allocator; once this space is exhausted allocations will fail.
|
||||
///
|
||||
/// @returns The size of the remaining unallocated space in bytes.
|
||||
size_t Remaining() const { return m_size - VoidPtrDiff(m_pCurrent, m_pStart); }
|
||||
|
||||
private:
|
||||
void* m_pStart; ///< Pointer to where the backing allocation starts.
|
||||
void* m_pCurrent; ///< Pointer to the current position of backing memory.
|
||||
void* m_pCommittedToPage; ///< Pointer to the end of the last committed page.
|
||||
|
||||
size_t m_size; ///< Size of the allocation.
|
||||
size_t m_pageSize; ///< OS' defined page size.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(VirtualLinearAllocator);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(VirtualLinearAllocator);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A "resource acquisition is initialization" (RAII) wrapper for the LinearAllocator classes.
|
||||
*
|
||||
* The RAII paradigm allows critical sections to be automatically acquired during this class' constructor, and
|
||||
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
||||
* this class for stack-allocated objects.
|
||||
*
|
||||
* This object will ensure that anything allocated the object is allocated on the stack and when it goes out of scope
|
||||
* will be properly "rewound" by the allocator. See the below example.
|
||||
*
|
||||
*
|
||||
* {
|
||||
* [Current pointer = 0x10]
|
||||
* LinearAllocatorAuto allocator(pPtrToAllocator);
|
||||
* Allocations occur ...
|
||||
* [Current pointer = 0x80]
|
||||
* }
|
||||
* [Current pointer rewinds = 0x10]
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <class LinearAllocator>
|
||||
class LinearAllocatorAuto
|
||||
{
|
||||
public:
|
||||
/// Tracks the current start pointer.
|
||||
///
|
||||
/// @param pAllocator The allocator to wrap.
|
||||
/// @param decommit Whether to decommit any pages of memory allocated when this goes out of scope.
|
||||
LinearAllocatorAuto(LinearAllocator* pAllocator, bool decommit)
|
||||
:
|
||||
m_pAllocator(pAllocator),
|
||||
#if PAL_MEMTRACK
|
||||
m_memTracker(pAllocator),
|
||||
#endif
|
||||
m_pStart(nullptr),
|
||||
m_decommit(decommit)
|
||||
{
|
||||
PAL_ASSERT(pAllocator != nullptr);
|
||||
m_pStart = m_pAllocator->Current();
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
Result result = m_memTracker.Init();
|
||||
PAL_ASSERT(result == Result::_Success);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Rewinds any allocations made when this goes out of scope.
|
||||
~LinearAllocatorAuto()
|
||||
{
|
||||
m_pAllocator->Rewind(m_pStart, m_decommit);
|
||||
}
|
||||
|
||||
/// Allocates a block of memory.
|
||||
///
|
||||
/// @param [in] allocInfo Contains information about the requested allocation.
|
||||
///
|
||||
/// @returns Pointer to the allocated memory, nullptr if the allocation failed.
|
||||
void* Alloc(const AllocInfo& allocInfo)
|
||||
{
|
||||
void* pMemory = nullptr;
|
||||
#if PAL_MEMTRACK
|
||||
pMemory = m_memTracker.Alloc(allocInfo);
|
||||
#else
|
||||
pMemory = m_pAllocator->Alloc(allocInfo);
|
||||
#endif
|
||||
|
||||
return pMemory;
|
||||
}
|
||||
|
||||
/// Frees a block of memory.
|
||||
///
|
||||
/// @param [in] freeInfo Contains information about the requested free.
|
||||
void Free(const FreeInfo& freeInfo)
|
||||
{
|
||||
#if PAL_MEMTRACK
|
||||
m_memTracker.Free(freeInfo);
|
||||
#else
|
||||
m_pAllocator->Free(freeInfo);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
LinearAllocator*const m_pAllocator; ///< The LinearAllocator which this object wraps.
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
MemTracker<LinearAllocator> m_memTracker; ///< Memory tracker for this LinearAllocatorAuto.
|
||||
#endif
|
||||
|
||||
void* m_pStart; ///< Where the LinearAllocator started when wrapped by this.
|
||||
const bool m_decommit; ///< Whether to decommit any pages of memory allocated on destruction.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(LinearAllocatorAuto);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(LinearAllocatorAuto);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A simple extension of VirtualLinearAllocator that contains an IntrusiveListNode pointing at itself.
|
||||
* This makes it very easy to create and manage IntrusiveLists of VirtualLinearAllocators.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class VirtualLinearAllocatorWithNode : public VirtualLinearAllocator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
VirtualLinearAllocatorWithNode(size_t size) : VirtualLinearAllocator(size), m_node(this) {}
|
||||
|
||||
/// Destructor.
|
||||
virtual ~VirtualLinearAllocatorWithNode() {}
|
||||
|
||||
/// Gets this linear allocator's associated IntrusiveListNode.
|
||||
///
|
||||
/// @returns Pointer to this allocator's associated IntrusiveListNode.
|
||||
IntrusiveListNode<VirtualLinearAllocatorWithNode>* GetNode() { return &m_node; }
|
||||
|
||||
private:
|
||||
IntrusiveListNode<VirtualLinearAllocatorWithNode> m_node;
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(VirtualLinearAllocatorWithNode);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(VirtualLinearAllocatorWithNode);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,226 +1,226 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMath.h
|
||||
* @brief PAL utility collection function/constant declarations for the Math sub-namespace.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palSysMemory.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Util sub-namespace defining several useful math routines and constants.
|
||||
namespace Math
|
||||
{
|
||||
|
||||
/// Exponent mask of a single-precision IEEE float.
|
||||
constexpr uint32 FloatExponentMask = 0x7F800000;
|
||||
/// Exponent bias of a single-precision IEEE float.
|
||||
constexpr uint32 FloatExponentBias = 127;
|
||||
/// Number of bits in the mantissa of a single-precision IEEE float.
|
||||
constexpr uint32 FloatNumMantissaBits = 23;
|
||||
/// Mantissa mask of a single-precision IEEE float.
|
||||
constexpr uint32 FloatMantissaMask = 0x007FFFFF;
|
||||
/// Sign bit mask of a single precision IEEE float.
|
||||
constexpr uint32 FloatSignBitMask = 0x80000000;
|
||||
/// Mask of all non-sign bits of a single-precision IEEE float.
|
||||
constexpr uint32 FloatMaskOutSignBit = 0x7FFFFFFF;
|
||||
/// Minimum number of float bits in a normalized IEE float.
|
||||
constexpr uint32 MinNormalizedFloatBits = 0x00800000;
|
||||
|
||||
/// Positive one.
|
||||
constexpr float FloatOne = 1.0f;
|
||||
/// Negative one.
|
||||
constexpr float FloatNegOne = -1.0f;
|
||||
/// Zero.
|
||||
constexpr float FloatZero = 0.0f;
|
||||
/// Positive infinity.
|
||||
constexpr float FloatInfinity = std::numeric_limits<float>::infinity();
|
||||
|
||||
/// Fraction structure.
|
||||
struct Fraction
|
||||
{
|
||||
uint32 num; ///< Numerator
|
||||
uint32 den; ///< Denominator
|
||||
};
|
||||
|
||||
/// Returns the bits of a floating point value as an unsigned integer.
|
||||
inline uint32 FloatToBits(float f)
|
||||
{
|
||||
return (*(reinterpret_cast<uint32*>(&f)));
|
||||
}
|
||||
|
||||
/// Assigns the bits contained in an unsigned integer to the float pointer location
|
||||
inline void SetBitsToFloat(float* f, uint32 u)
|
||||
{
|
||||
*(reinterpret_cast<uint32*>(f)) = u;
|
||||
}
|
||||
|
||||
/// Returns true if the specified float is denormalized.
|
||||
extern bool IsDenorm(float f);
|
||||
/// Returns true if the specified float is +/- infinity.
|
||||
extern bool IsInf(float f);
|
||||
/// Returns true if the specified float is a NaN.
|
||||
extern bool IsNaN(float f);
|
||||
|
||||
/// Determines if a floating-point number is either +/-Infinity or NaN.
|
||||
inline bool IsInfOrNaN(float f)
|
||||
{
|
||||
return (IsInf(f) || IsNaN(f));
|
||||
}
|
||||
|
||||
/// @brief Converts a floating point number to a signed fixed point number with the given integer and fractional bits.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [-1.0, 1.0]. If the
|
||||
/// intBits is zero, the fracBits is assumed to include 1 sign bit, otherwise the sign bit is assumed to be part of the
|
||||
/// intBits. A typical use for enableRounding would be when converting SNORM/UNORM values to fixed point.
|
||||
///
|
||||
/// @param [in] f Floating point value to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit) in the fixed point output.
|
||||
/// @param [in] fracBits Number of fractional bits in the fixed point output.
|
||||
/// @param [in] enableRounding Round before conversion.
|
||||
///
|
||||
/// @returns Fixed point number in a uint32.
|
||||
extern uint32 FloatToSFixed(float f, uint32 intBits, uint32 fracBits, bool enableRounding = false);
|
||||
|
||||
/// @brief Converts a floating point number to an unsigned fixed point number with the given integer and
|
||||
/// fractional bits.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [-1.0, 1.0]. A typical use
|
||||
/// for enableRounding would be when converting SNORM/UNORM values to fixed point.
|
||||
///
|
||||
/// @param [in] f Floating point value to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit) in the fixed point output.
|
||||
/// @param [in] fracBits Number of fractional bits in the fixed point output.
|
||||
/// @param [in] enableRounding Round before conversion.
|
||||
///
|
||||
/// @returns Fixed point number in a uint32.
|
||||
extern uint32 FloatToUFixed(float f, uint32 intBits, uint32 fracBits, bool enableRounding = false);
|
||||
|
||||
/// @brief Converts a signed fixed point number with the given integer and fractional bits to a floating point number.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [-1.0, 1.0]. If numIntBits
|
||||
/// is 0, numFracBits is assumed to have 1 bit for the sign, otherwise the sign bit is assumed to be part of the integer
|
||||
/// bits.
|
||||
///
|
||||
/// @param [in] fixedPtNum Fixed point number to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit).
|
||||
/// @param [in] fracBits Number of fractional bits.
|
||||
///
|
||||
/// @returns Converted floating point number.
|
||||
extern float SFixedToFloat(int32 fixedPtNum, uint32 intBits, uint32 fracBits);
|
||||
|
||||
/// @brief Converts a unsigned fixed point number with the given integer and fractional bits to a floating point number.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [0, 1.0].
|
||||
///
|
||||
/// @param [in] fixedPtNum Fixed point number to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit).
|
||||
/// @param [in] fracBits Number of fractional bits.
|
||||
///
|
||||
/// @returns Converted floating point number.
|
||||
extern float UFixedToFloat(uint32 fixedPtNum, uint32 intBits, uint32 fracBits);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a 16-bit signed floating point number.
|
||||
extern uint32 Float32ToFloat16(float f);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to an 11-bit signed floating point number.
|
||||
extern uint32 Float32ToFloat11(float f);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a 10-bit signed floating point number.
|
||||
extern uint32 Float32ToFloat10(float f);
|
||||
|
||||
/// Converts a 32-bit IEEE floating-point number to a 10-bit unsigned floating-point number.
|
||||
extern uint32 Float32ToFloat10_6e4(float f);
|
||||
|
||||
/// Converts a 10-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float10_6e4ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a N-bit signed floating point number.
|
||||
extern uint32 Float32ToNumBits(float float32, uint32 numBits);
|
||||
|
||||
/// Converts a 16-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float16ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts an 11-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float11ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts a 10-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float10ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts an N-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float FloatNumBitsToFloat32(uint32 input, uint32 numBits);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a fraction.
|
||||
extern Fraction Float32ToFraction(float float32);
|
||||
|
||||
/// Returns the square root of the specified value.
|
||||
extern float Sqrt(float f);
|
||||
|
||||
/// Returns the result of an exponent operation (base^exponent).
|
||||
extern float Pow(float base, float exponent);
|
||||
|
||||
/// Returns the unsigned integer absolute value.
|
||||
extern uint32 Absu(int32 number);
|
||||
|
||||
/// Return sign-preserved zero if input is denorm, otherwise input value
|
||||
extern float FlushDenormToZero(float input);
|
||||
|
||||
/// Return value in 1.7 signed magnitude format. Valid input range is (-127, 127)
|
||||
extern uint8 IntToSignedMagnitude(int8 input);
|
||||
|
||||
/// @brief Performs unsigned fixed-point rounding operation.
|
||||
///
|
||||
/// @param [in] value Fixed point number to convert in Qm.f format.
|
||||
/// @param [in] n Number of fractional bits.
|
||||
///
|
||||
/// @returns rounded fixed point number in Q0 format (unsigned integer).
|
||||
constexpr uint32 UFixedRoundToUint32(uint32 value, uint8 n)
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((0 < n) && (n < 31));
|
||||
return ((value + (((1 << n) >> 1))) >> n);
|
||||
}
|
||||
|
||||
/// @brief Performs signed fixed-point rounding operation.
|
||||
///
|
||||
/// @param [in] value Fixed point number to convert in Qm.f format.
|
||||
/// @param [in] n Number of fractional bits.
|
||||
///
|
||||
/// @returns rounded fixed point number in Q0 format (signed integer).
|
||||
constexpr int32 SFixedRoundToInt32(int32 value, uint8 n)
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((0 < n) && (n < 30));
|
||||
return ((value + (((1 << n) >> 1))) >> n);
|
||||
}
|
||||
|
||||
} // Math
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMath.h
|
||||
* @brief PAL utility collection function/constant declarations for the Math sub-namespace.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palSysMemory.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Util sub-namespace defining several useful math routines and constants.
|
||||
namespace Math
|
||||
{
|
||||
|
||||
/// Exponent mask of a single-precision IEEE float.
|
||||
constexpr uint32 FloatExponentMask = 0x7F800000;
|
||||
/// Exponent bias of a single-precision IEEE float.
|
||||
constexpr uint32 FloatExponentBias = 127;
|
||||
/// Number of bits in the mantissa of a single-precision IEEE float.
|
||||
constexpr uint32 FloatNumMantissaBits = 23;
|
||||
/// Mantissa mask of a single-precision IEEE float.
|
||||
constexpr uint32 FloatMantissaMask = 0x007FFFFF;
|
||||
/// Sign bit mask of a single precision IEEE float.
|
||||
constexpr uint32 FloatSignBitMask = 0x80000000;
|
||||
/// Mask of all non-sign bits of a single-precision IEEE float.
|
||||
constexpr uint32 FloatMaskOutSignBit = 0x7FFFFFFF;
|
||||
/// Minimum number of float bits in a normalized IEE float.
|
||||
constexpr uint32 MinNormalizedFloatBits = 0x00800000;
|
||||
|
||||
/// Positive one.
|
||||
constexpr float FloatOne = 1.0f;
|
||||
/// Negative one.
|
||||
constexpr float FloatNegOne = -1.0f;
|
||||
/// Zero.
|
||||
constexpr float FloatZero = 0.0f;
|
||||
/// Positive infinity.
|
||||
constexpr float FloatInfinity = std::numeric_limits<float>::infinity();
|
||||
|
||||
/// Fraction structure.
|
||||
struct Fraction
|
||||
{
|
||||
uint32 num; ///< Numerator
|
||||
uint32 den; ///< Denominator
|
||||
};
|
||||
|
||||
/// Returns the bits of a floating point value as an unsigned integer.
|
||||
inline uint32 FloatToBits(float f)
|
||||
{
|
||||
return (*(reinterpret_cast<uint32*>(&f)));
|
||||
}
|
||||
|
||||
/// Assigns the bits contained in an unsigned integer to the float pointer location
|
||||
inline void SetBitsToFloat(float* f, uint32 u)
|
||||
{
|
||||
*(reinterpret_cast<uint32*>(f)) = u;
|
||||
}
|
||||
|
||||
/// Returns true if the specified float is denormalized.
|
||||
extern bool IsDenorm(float f);
|
||||
/// Returns true if the specified float is +/- infinity.
|
||||
extern bool IsInf(float f);
|
||||
/// Returns true if the specified float is a NaN.
|
||||
extern bool IsNaN(float f);
|
||||
|
||||
/// Determines if a floating-point number is either +/-Infinity or NaN.
|
||||
inline bool IsInfOrNaN(float f)
|
||||
{
|
||||
return (IsInf(f) || IsNaN(f));
|
||||
}
|
||||
|
||||
/// @brief Converts a floating point number to a signed fixed point number with the given integer and fractional bits.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [-1.0, 1.0]. If the
|
||||
/// intBits is zero, the fracBits is assumed to include 1 sign bit, otherwise the sign bit is assumed to be part of the
|
||||
/// intBits. A typical use for enableRounding would be when converting SNORM/UNORM values to fixed point.
|
||||
///
|
||||
/// @param [in] f Floating point value to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit) in the fixed point output.
|
||||
/// @param [in] fracBits Number of fractional bits in the fixed point output.
|
||||
/// @param [in] enableRounding Round before conversion.
|
||||
///
|
||||
/// @returns Fixed point number in a uint32.
|
||||
extern uint32 FloatToSFixed(float f, uint32 intBits, uint32 fracBits, bool enableRounding = false);
|
||||
|
||||
/// @brief Converts a floating point number to an unsigned fixed point number with the given integer and
|
||||
/// fractional bits.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [-1.0, 1.0]. A typical use
|
||||
/// for enableRounding would be when converting SNORM/UNORM values to fixed point.
|
||||
///
|
||||
/// @param [in] f Floating point value to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit) in the fixed point output.
|
||||
/// @param [in] fracBits Number of fractional bits in the fixed point output.
|
||||
/// @param [in] enableRounding Round before conversion.
|
||||
///
|
||||
/// @returns Fixed point number in a uint32.
|
||||
extern uint32 FloatToUFixed(float f, uint32 intBits, uint32 fracBits, bool enableRounding = false);
|
||||
|
||||
/// @brief Converts a signed fixed point number with the given integer and fractional bits to a floating point number.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [-1.0, 1.0]. If numIntBits
|
||||
/// is 0, numFracBits is assumed to have 1 bit for the sign, otherwise the sign bit is assumed to be part of the integer
|
||||
/// bits.
|
||||
///
|
||||
/// @param [in] fixedPtNum Fixed point number to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit).
|
||||
/// @param [in] fracBits Number of fractional bits.
|
||||
///
|
||||
/// @returns Converted floating point number.
|
||||
extern float SFixedToFloat(int32 fixedPtNum, uint32 intBits, uint32 fracBits);
|
||||
|
||||
/// @brief Converts a unsigned fixed point number with the given integer and fractional bits to a floating point number.
|
||||
///
|
||||
/// If the number of integer bits is zero, the incoming value is treated as normalized, i.e. [0, 1.0].
|
||||
///
|
||||
/// @param [in] fixedPtNum Fixed point number to convert.
|
||||
/// @param [in] intBits Number of integer bits (including the sign bit).
|
||||
/// @param [in] fracBits Number of fractional bits.
|
||||
///
|
||||
/// @returns Converted floating point number.
|
||||
extern float UFixedToFloat(uint32 fixedPtNum, uint32 intBits, uint32 fracBits);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a 16-bit signed floating point number.
|
||||
extern uint32 Float32ToFloat16(float f);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to an 11-bit signed floating point number.
|
||||
extern uint32 Float32ToFloat11(float f);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a 10-bit signed floating point number.
|
||||
extern uint32 Float32ToFloat10(float f);
|
||||
|
||||
/// Converts a 32-bit IEEE floating-point number to a 10-bit unsigned floating-point number.
|
||||
extern uint32 Float32ToFloat10_6e4(float f);
|
||||
|
||||
/// Converts a 10-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float10_6e4ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a N-bit signed floating point number.
|
||||
extern uint32 Float32ToNumBits(float float32, uint32 numBits);
|
||||
|
||||
/// Converts a 16-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float16ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts an 11-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float11ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts a 10-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float Float10ToFloat32(uint32 fBits);
|
||||
|
||||
/// Converts an N-bit signed floating point number to a 32-bit IEEE floating point number.
|
||||
extern float FloatNumBitsToFloat32(uint32 input, uint32 numBits);
|
||||
|
||||
/// Converts a 32-bit IEEE floating point number to a fraction.
|
||||
extern Fraction Float32ToFraction(float float32);
|
||||
|
||||
/// Returns the square root of the specified value.
|
||||
extern float Sqrt(float f);
|
||||
|
||||
/// Returns the result of an exponent operation (base^exponent).
|
||||
extern float Pow(float base, float exponent);
|
||||
|
||||
/// Returns the unsigned integer absolute value.
|
||||
extern uint32 Absu(int32 number);
|
||||
|
||||
/// Return sign-preserved zero if input is denorm, otherwise input value
|
||||
extern float FlushDenormToZero(float input);
|
||||
|
||||
/// Return value in 1.7 signed magnitude format. Valid input range is (-127, 127)
|
||||
extern uint8 IntToSignedMagnitude(int8 input);
|
||||
|
||||
/// @brief Performs unsigned fixed-point rounding operation.
|
||||
///
|
||||
/// @param [in] value Fixed point number to convert in Qm.f format.
|
||||
/// @param [in] n Number of fractional bits.
|
||||
///
|
||||
/// @returns rounded fixed point number in Q0 format (unsigned integer).
|
||||
constexpr uint32 UFixedRoundToUint32(uint32 value, uint8 n)
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((0 < n) && (n < 31));
|
||||
return ((value + (((1 << n) >> 1))) >> n);
|
||||
}
|
||||
|
||||
/// @brief Performs signed fixed-point rounding operation.
|
||||
///
|
||||
/// @param [in] value Fixed point number to convert in Qm.f format.
|
||||
/// @param [in] n Number of fractional bits.
|
||||
///
|
||||
/// @returns rounded fixed point number in Q0 format (signed integer).
|
||||
constexpr int32 SFixedRoundToInt32(int32 value, uint8 n)
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((0 < n) && (n < 30));
|
||||
return ((value + (((1 << n) >> 1))) >> n);
|
||||
}
|
||||
|
||||
} // Math
|
||||
} // Util
|
||||
|
||||
@@ -1,158 +1,158 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMemTracker.h
|
||||
* @brief PAL utility collection MemTracker class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
|
||||
#include "palIntrusiveList.h"
|
||||
#include "palMutex.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
struct AllocInfo;
|
||||
struct FreeInfo;
|
||||
struct MemTrackerElem;
|
||||
enum SystemAllocType : uint32;
|
||||
|
||||
/// @internal
|
||||
///
|
||||
/// An alloc-less list used by the MemTracker to keep track of all allocations.
|
||||
typedef IntrusiveList<MemTrackerElem> MemTrackerList;
|
||||
|
||||
/// @internal
|
||||
///
|
||||
/// Specifies whether a particular memory block was allocated with PAL_MALLOC/PAL_CALLOC, PAL_NEW, or PAL_NEW_ARRAY.
|
||||
/// Used to verify correct matching with PAL_FREE, PAL_DELETE, and PAL_DELETE_ARRAY.
|
||||
enum class MemBlkType : uint32
|
||||
{
|
||||
Malloc = 0,
|
||||
New,
|
||||
NewArray,
|
||||
};
|
||||
|
||||
/// @internal
|
||||
///
|
||||
/// Internal structure used by MemTracker to store information on each allocation.
|
||||
struct MemTrackerElem
|
||||
{
|
||||
size_t size; ///< Size of allocation request.
|
||||
MemBlkType blockType; ///< Memory block type (malloc, new, new array).
|
||||
const char* pFilename; ///< File that requested allocation.
|
||||
uint32 lineNumber; ///< Line number that requested allocation.
|
||||
void* pClientMem; ///< Starting "client usable" data address.
|
||||
void* pOrigMem; ///< Original address of the allocation returned from our underlying allocator.
|
||||
size_t allocNum; ///< The number of the memory allocation. 1 based.
|
||||
MemTrackerList* pList; ///< The list this struct is in. It helps check which MemTracker owns this struct.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Class responsible for tracking allocations and frees to notify the developer of memory leaks.
|
||||
*
|
||||
* Tracking is enabled/disabled via the PAL_MEMTRACK define.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class MemTracker
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
MemTracker(Allocator*const pAllocator);
|
||||
~MemTracker();
|
||||
|
||||
/// Performs any non-safe initialization that cannot be done in the constructor.
|
||||
///
|
||||
/// @returns Result::Success if initialization is successful, otherwise an appropriate error.
|
||||
Result Init();
|
||||
|
||||
/// Allocates a block of memory and track it using the memory tracker.
|
||||
///
|
||||
/// @param [in] allocInfo Contains information about the requested allocation.
|
||||
///
|
||||
/// @returns Pointer to the allocated memory, nullptr if the allocation failed.
|
||||
void* Alloc(
|
||||
const AllocInfo& allocInfo);
|
||||
|
||||
/// Frees a block of memory.
|
||||
///
|
||||
/// @param [in] freeInfo Contains information about the requested free.
|
||||
void Free(
|
||||
const FreeInfo& freeInfo);
|
||||
|
||||
private:
|
||||
void* AddMemElement(
|
||||
void* pMem,
|
||||
size_t bytes,
|
||||
size_t align,
|
||||
MemBlkType blockType,
|
||||
const char* pFilename,
|
||||
uint32 lineNumber);
|
||||
|
||||
void* RemoveMemElement(void* pMem, MemBlkType blockType);
|
||||
|
||||
void MemoryReport();
|
||||
void FreeLeakedMemory();
|
||||
|
||||
// Sentinel patterns used to detect memory underrun.
|
||||
static constexpr uint32 UnderrunSentinel = 0xDEADBEEF;
|
||||
// Sentinel patterns used to detect memory overrun.
|
||||
static constexpr uint32 OverrunSentinel = 0xCAFEBABE;
|
||||
|
||||
// Size of markers for underruns/overruns. Setting this to 0 disables this feature.
|
||||
static constexpr size_t MarkerSizeUints = PAL_CACHE_LINE_BYTES / sizeof(uint32);
|
||||
|
||||
// Size of underrun/overrun markers in bytes.
|
||||
static constexpr size_t MarkerSizeBytes = MarkerSizeUints * sizeof(uint32);
|
||||
|
||||
MemTrackerList m_trackerList; // The list of active allocations.
|
||||
Mutex m_mutex; // Serializes access to list of active allocations.
|
||||
|
||||
const size_t m_markerSizeUints; // Member variable copy of MarkerSizeUints. Only used to prevent compiler
|
||||
// warnings when MarkerSizeUints is 0.
|
||||
const size_t m_markerSizeBytes; // Member variable copy of MarkerSizeBytes. Only used to prevent compiler
|
||||
// warnings when MarkerSizeBytes is 0.
|
||||
|
||||
Allocator*const m_pAllocator; // Allocator for performing the actual allocations.
|
||||
|
||||
size_t m_nextAllocNum; // The allocation number that the next allocated block will receive.
|
||||
const size_t m_breakOnAllocNum; // The allocation number to trigger a debug break on.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(MemTracker);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
#endif
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMemTracker.h
|
||||
* @brief PAL utility collection MemTracker class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
|
||||
#include "palIntrusiveList.h"
|
||||
#include "palMutex.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
struct AllocInfo;
|
||||
struct FreeInfo;
|
||||
struct MemTrackerElem;
|
||||
enum SystemAllocType : uint32;
|
||||
|
||||
/// @internal
|
||||
///
|
||||
/// An alloc-less list used by the MemTracker to keep track of all allocations.
|
||||
typedef IntrusiveList<MemTrackerElem> MemTrackerList;
|
||||
|
||||
/// @internal
|
||||
///
|
||||
/// Specifies whether a particular memory block was allocated with PAL_MALLOC/PAL_CALLOC, PAL_NEW, or PAL_NEW_ARRAY.
|
||||
/// Used to verify correct matching with PAL_FREE, PAL_DELETE, and PAL_DELETE_ARRAY.
|
||||
enum class MemBlkType : uint32
|
||||
{
|
||||
Malloc = 0,
|
||||
New,
|
||||
NewArray,
|
||||
};
|
||||
|
||||
/// @internal
|
||||
///
|
||||
/// Internal structure used by MemTracker to store information on each allocation.
|
||||
struct MemTrackerElem
|
||||
{
|
||||
size_t size; ///< Size of allocation request.
|
||||
MemBlkType blockType; ///< Memory block type (malloc, new, new array).
|
||||
const char* pFilename; ///< File that requested allocation.
|
||||
uint32 lineNumber; ///< Line number that requested allocation.
|
||||
void* pClientMem; ///< Starting "client usable" data address.
|
||||
void* pOrigMem; ///< Original address of the allocation returned from our underlying allocator.
|
||||
size_t allocNum; ///< The number of the memory allocation. 1 based.
|
||||
MemTrackerList* pList; ///< The list this struct is in. It helps check which MemTracker owns this struct.
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Class responsible for tracking allocations and frees to notify the developer of memory leaks.
|
||||
*
|
||||
* Tracking is enabled/disabled via the PAL_MEMTRACK define.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class MemTracker
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
MemTracker(Allocator*const pAllocator);
|
||||
~MemTracker();
|
||||
|
||||
/// Performs any non-safe initialization that cannot be done in the constructor.
|
||||
///
|
||||
/// @returns Result::Success if initialization is successful, otherwise an appropriate error.
|
||||
Result Init();
|
||||
|
||||
/// Allocates a block of memory and track it using the memory tracker.
|
||||
///
|
||||
/// @param [in] allocInfo Contains information about the requested allocation.
|
||||
///
|
||||
/// @returns Pointer to the allocated memory, nullptr if the allocation failed.
|
||||
void* Alloc(
|
||||
const AllocInfo& allocInfo);
|
||||
|
||||
/// Frees a block of memory.
|
||||
///
|
||||
/// @param [in] freeInfo Contains information about the requested free.
|
||||
void Free(
|
||||
const FreeInfo& freeInfo);
|
||||
|
||||
private:
|
||||
void* AddMemElement(
|
||||
void* pMem,
|
||||
size_t bytes,
|
||||
size_t align,
|
||||
MemBlkType blockType,
|
||||
const char* pFilename,
|
||||
uint32 lineNumber);
|
||||
|
||||
void* RemoveMemElement(void* pMem, MemBlkType blockType);
|
||||
|
||||
void MemoryReport();
|
||||
void FreeLeakedMemory();
|
||||
|
||||
// Sentinel patterns used to detect memory underrun.
|
||||
static constexpr uint32 UnderrunSentinel = 0xDEADBEEF;
|
||||
// Sentinel patterns used to detect memory overrun.
|
||||
static constexpr uint32 OverrunSentinel = 0xCAFEBABE;
|
||||
|
||||
// Size of markers for underruns/overruns. Setting this to 0 disables this feature.
|
||||
static constexpr size_t MarkerSizeUints = PAL_CACHE_LINE_BYTES / sizeof(uint32);
|
||||
|
||||
// Size of underrun/overrun markers in bytes.
|
||||
static constexpr size_t MarkerSizeBytes = MarkerSizeUints * sizeof(uint32);
|
||||
|
||||
MemTrackerList m_trackerList; // The list of active allocations.
|
||||
Mutex m_mutex; // Serializes access to list of active allocations.
|
||||
|
||||
const size_t m_markerSizeUints; // Member variable copy of MarkerSizeUints. Only used to prevent compiler
|
||||
// warnings when MarkerSizeUints is 0.
|
||||
const size_t m_markerSizeBytes; // Member variable copy of MarkerSizeBytes. Only used to prevent compiler
|
||||
// warnings when MarkerSizeBytes is 0.
|
||||
|
||||
Allocator*const m_pAllocator; // Allocator for performing the actual allocations.
|
||||
|
||||
size_t m_nextAllocNum; // The allocation number that the next allocated block will receive.
|
||||
const size_t m_breakOnAllocNum; // The allocation number to trigger a debug break on.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(MemTracker);
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,331 +1,331 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMemTrackerImpl.h
|
||||
* @brief PAL utility collection MemTracker class implementations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
|
||||
#include "palIntrusiveListImpl.h"
|
||||
#include "palMemTracker.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Table to convert a blockType to a string. Used by the logging routines.
|
||||
constexpr const char* MemBlkTypeStr[] =
|
||||
{
|
||||
"Malloc", ///< MemBlkType::Malloc
|
||||
"New", ///< MemBlkType::New
|
||||
"NewArray", ///< MemBlkType::NewArray
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template <typename Allocator>
|
||||
MemTracker<Allocator>::MemTracker(
|
||||
Allocator*const pAllocator)
|
||||
:
|
||||
m_markerSizeUints(MarkerSizeUints),
|
||||
m_markerSizeBytes(MarkerSizeBytes),
|
||||
m_pAllocator(pAllocator),
|
||||
m_nextAllocNum(1),
|
||||
m_breakOnAllocNum(0)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template <typename Allocator>
|
||||
MemTracker<Allocator>::~MemTracker()
|
||||
{
|
||||
// Clean-up leaked memory if needed
|
||||
if (m_trackerList.IsEmpty() == false)
|
||||
{
|
||||
// If the list isn't empty, we have a leak. The leak could either be caused by an internal PAL leak,
|
||||
// a client leak, or even the application not destroying API objects.
|
||||
PAL_ALERT_ALWAYS();
|
||||
|
||||
// Dump out a list of unfreed blocks.
|
||||
MemoryReport();
|
||||
|
||||
FreeLeakedMemory();
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template <typename Allocator>
|
||||
Result MemTracker<Allocator>::Init()
|
||||
{
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Adds the newly allocated memory block to the list of blocks for tracking.
|
||||
//
|
||||
// The tracking information includes things like filename, line numbers, and type of block. Also, given a pointer,
|
||||
// adds the Underrun/Overrun markers to the memory allocated, and return a pointer to the actual client usable memory.
|
||||
//
|
||||
// See MemTracker::Alloc() which is used to allocate memory that is being tracked.
|
||||
template <typename Allocator>
|
||||
void* MemTracker<Allocator>::AddMemElement(
|
||||
void* pMem, // [in,out] Original pointer allocated by MemTracker::Alloc.
|
||||
size_t bytes, // Client requested allocation size in bytes.
|
||||
size_t align, // The max of the client-requested alignment or the internal alignment, in bytes.
|
||||
MemBlkType blockType, // Block type based on calling allocation routine.
|
||||
const char* pFilename, // Client filename that is requesting the memory.
|
||||
uint32 lineNumber) // Line number in client file that is requesting the memory.
|
||||
{
|
||||
// Our internal data is all relative to the client pointer so find that first. See Alloc for more details.
|
||||
// (align1)(MemTrackerList::Node)(MemTrackerElem)(underflow tracker)(client allocation)(align2)(overflow tracker)
|
||||
constexpr size_t InternalSize = sizeof(MemTrackerList::Node) + sizeof(MemTrackerElem);
|
||||
|
||||
void*const pClientMem = VoidPtrAlign(VoidPtrInc(pMem, m_markerSizeBytes + InternalSize), align);
|
||||
uint32* pUnderrun = static_cast<uint32*>(VoidPtrDec(pClientMem, m_markerSizeBytes));
|
||||
uint32* pOverrun = static_cast<uint32*>(VoidPtrInc(pClientMem, Pow2Align(bytes, sizeof(uint32))));
|
||||
|
||||
auto*const pNewElement = static_cast<MemTrackerElem*>(VoidPtrDec(pUnderrun, sizeof(MemTrackerElem)));
|
||||
void*const pNewNodeMem = VoidPtrDec(pNewElement, sizeof(MemTrackerList::Node));
|
||||
auto*const pNewNode = PAL_PLACEMENT_NEW(pNewNodeMem) MemTrackerList::Node(pNewElement);
|
||||
|
||||
// Mark the memory with the underrun/overrun marker.
|
||||
for (uint32 markerUints = 0; markerUints < m_markerSizeUints; ++markerUints)
|
||||
{
|
||||
*pUnderrun++ = UnderrunSentinel;
|
||||
*pOverrun++ = OverrunSentinel;
|
||||
}
|
||||
|
||||
pNewElement->size = bytes;
|
||||
pNewElement->pFilename = pFilename;
|
||||
pNewElement->lineNumber = lineNumber;
|
||||
pNewElement->blockType = blockType;
|
||||
pNewElement->pClientMem = pClientMem;
|
||||
pNewElement->pOrigMem = pMem;
|
||||
pNewElement->pList = &m_trackerList;
|
||||
|
||||
MutexAuto lock(&m_mutex);
|
||||
|
||||
// Trigger an assert if we're about to allocate the break-on-allocation number.
|
||||
if (m_nextAllocNum == m_breakOnAllocNum)
|
||||
{
|
||||
PAL_ASSERT_ALWAYS();
|
||||
}
|
||||
|
||||
pNewElement->allocNum = m_nextAllocNum;
|
||||
++m_nextAllocNum;
|
||||
|
||||
m_trackerList.PushFront(pNewNode);
|
||||
|
||||
return pClientMem;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Removes an allocated block from the list of blocks used for tracking.
|
||||
//
|
||||
// The routine checks for invalid frees (and duplicate frees). Also, the routine is able to detect mismatched alloc/free
|
||||
// usage based on the blockType. The routine is called with the pointer to the client usable memory and returns the
|
||||
// pointer to the allocated memory.
|
||||
//
|
||||
// See MemTracker::Free() which is used to free memory that is being tracked.
|
||||
template <typename Allocator>
|
||||
void* MemTracker<Allocator>::RemoveMemElement(
|
||||
void* pClientMem, // Pointer to client usable memory.
|
||||
MemBlkType blockType) // Block type based on calling deallocation routine.
|
||||
{
|
||||
void* pOrigPtr = nullptr;
|
||||
|
||||
// Recall that this is our internal memory layout. See Alloc for more details.
|
||||
// (align1)(MemTrackerList::Node)(MemTrackerElem)(underflow tracker)(client allocation)(align2)(overflow tracker)
|
||||
uint32* pUnderrun = static_cast<uint32*>(VoidPtrDec(pClientMem, m_markerSizeBytes));
|
||||
auto*const pCurrent = static_cast<MemTrackerElem*>(VoidPtrDec(pUnderrun, sizeof(MemTrackerElem)));
|
||||
auto*const pCurrentNode = static_cast<MemTrackerList::Node*>(VoidPtrDec(pCurrent, sizeof(MemTrackerList::Node)));
|
||||
uint32* pOverrun = static_cast<uint32*>(VoidPtrInc(pClientMem, Pow2Align(pCurrent->size, sizeof(uint32))));
|
||||
|
||||
// We should not be trying to free something twice or trying to free something which has not been allocated
|
||||
// by this MemTracker. We can verify both of these things by checking that the tracker's pList is equal to the
|
||||
// MemTracker's list.
|
||||
if (pCurrent->pList != &m_trackerList)
|
||||
{
|
||||
// A free was attempted on an unrecognized pointer.
|
||||
PAL_DPERROR("Invalid Free Attempted with ptr = : (%#x)", pClientMem);
|
||||
}
|
||||
else if (pCurrent->blockType != blockType)
|
||||
{
|
||||
// We have a mismatch in the alloc/free pair, e.g. PAL_NEW with PAL_FREE etc. return early here without freeing
|
||||
// the memory so it shows up as a leak.
|
||||
PAL_DPERROR("Trying to Free %s as %s.",
|
||||
MemBlkTypeStr[static_cast<uint32>(pCurrent->blockType)],
|
||||
MemBlkTypeStr[static_cast<uint32>(blockType)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We should check for memory corruption due to overflow or underflow before continuing because any underflow
|
||||
// might indicate that our internal state is corrupted. This could lead to a crash in the code below.
|
||||
for (uint32 markerUints = 0; markerUints < m_markerSizeUints; ++markerUints)
|
||||
{
|
||||
PAL_ASSERT(*pUnderrun++ == UnderrunSentinel);
|
||||
PAL_ASSERT(*pOverrun++ == OverrunSentinel);
|
||||
}
|
||||
|
||||
// Remove our tracker from the list and set it's pList to null to detect a double-free in the future.
|
||||
MutexAuto lock(&m_mutex);
|
||||
|
||||
m_trackerList.Erase(pCurrentNode);
|
||||
|
||||
pCurrent->pList = nullptr;
|
||||
pOrigPtr = pCurrent->pOrigMem;
|
||||
}
|
||||
|
||||
// Return a pointer to the actual allocated block.
|
||||
return pOrigPtr;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Allocates a block of memory and tracks it using the memory tracker.
|
||||
template <typename Allocator>
|
||||
void* MemTracker<Allocator>::Alloc(
|
||||
const AllocInfo& allocInfo)
|
||||
{
|
||||
// Allocating zero bytes of memory results in undefined behavior.
|
||||
PAL_ASSERT(allocInfo.bytes > 0);
|
||||
|
||||
void* pMem = nullptr;
|
||||
|
||||
// We want to allocate extra memory from the caller's allocator, in this layout:
|
||||
// (align1)(MemTrackerList::Node)(MemTrackerElem)(underflow tracker)(client allocation)(align2)(overflow tracker)
|
||||
// Here's why we need each of those sections:
|
||||
// 1. align1 is zero or more bytes needed to align the client allocation and our internal data.
|
||||
// 2. The MemTrackerList::Node object, which is used to link this allocation into m_trackerList.
|
||||
// 3. The MemTrackerElem struct contains bookkeeping data we need to report memory errors.
|
||||
// 4. The underflow and overflow trackers detect out of bounds writes. They are optional.
|
||||
// 5. The client allocation, which is actually returned to the caller.
|
||||
// 6. align2 is zero or more bytes needed to DWORD-align the overflow tracker.
|
||||
constexpr size_t InternalAlignment = Max(alignof(MemTrackerList::Node), alignof(MemTrackerElem));
|
||||
const size_t paddedAlignBytes = Max(allocInfo.alignment, InternalAlignment);
|
||||
const size_t paddedSizeBytes = (paddedAlignBytes + // 1
|
||||
sizeof(MemTrackerList::Node) + // 2
|
||||
sizeof(MemTrackerElem) + // 3
|
||||
m_markerSizeBytes + // 4.a
|
||||
Pow2Align(allocInfo.bytes, sizeof(uint32)) + // 5 & 6
|
||||
m_markerSizeBytes); // 4.b
|
||||
|
||||
const AllocInfo memTrackerInfo(paddedSizeBytes, paddedAlignBytes, allocInfo.zeroMem, allocInfo.allocType,
|
||||
allocInfo.blockType, allocInfo.pFilename, allocInfo.lineNumber);
|
||||
|
||||
pMem = m_pAllocator->Alloc(memTrackerInfo);
|
||||
|
||||
if (pMem != nullptr)
|
||||
{
|
||||
// Don't bother adding a failed allocation to the Memtrack list.
|
||||
pMem = AddMemElement(pMem,
|
||||
allocInfo.bytes,
|
||||
paddedAlignBytes,
|
||||
allocInfo.blockType,
|
||||
allocInfo.pFilename,
|
||||
allocInfo.lineNumber);
|
||||
}
|
||||
|
||||
return pMem;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Frees a block of memory. The routine is called with the pointer to the client usable memory.
|
||||
//
|
||||
// See MemTracker::RemoveMemElement() which is used to validate the free.
|
||||
template <typename Allocator>
|
||||
void MemTracker<Allocator>::Free(
|
||||
const FreeInfo& freeInfo)
|
||||
{
|
||||
// Don't want to call RemoveMemElement if the ptr is null.
|
||||
if (freeInfo.pClientMem != nullptr)
|
||||
{
|
||||
void* pMem = RemoveMemElement(freeInfo.pClientMem, freeInfo.blockType);
|
||||
|
||||
// If this free call is valid (RemoveMemElement doesn't return nullptr), release the memory.
|
||||
if (pMem != nullptr)
|
||||
{
|
||||
m_pAllocator->Free(FreeInfo(pMem, freeInfo.blockType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Frees all memory that has not been explicitly freed (in other words, memory that has leaked). This function is only
|
||||
// expected to be called when the memory tracker is being destroyed.
|
||||
template <typename Allocator>
|
||||
void MemTracker<Allocator>::FreeLeakedMemory()
|
||||
{
|
||||
for (MemTrackerList::Iter iter = m_trackerList.Begin(); iter.IsValid(); )
|
||||
{
|
||||
MemTrackerElem*const pCurrent = iter.Get();
|
||||
|
||||
// Free will release the memory for tracking and the actual element. This will invalidate our list iterator
|
||||
// unless we advance the iterator first.
|
||||
iter.Next();
|
||||
|
||||
Free(FreeInfo(pCurrent->pClientMem, pCurrent->blockType));
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Outputs information about leaked memory by traversing the memory tracker list.
|
||||
template <typename Allocator>
|
||||
void MemTracker<Allocator>::MemoryReport()
|
||||
{
|
||||
// When this env var is set to non-zero, don't report leaks.
|
||||
// Useful for crashing apps that don't give us a chance to clean up.
|
||||
const char* pToggle = getenv("AMDPAL_NO_LEAK_REPORT");
|
||||
|
||||
if ((pToggle == nullptr) || (atoi(pToggle) == 0))
|
||||
{
|
||||
PAL_DPWARN("================ List of Leaked Blocks ================");
|
||||
|
||||
for (MemTrackerList::Iter iter = m_trackerList.Begin(); iter.IsValid(); iter.Next())
|
||||
{
|
||||
MemTrackerElem*const pCurrent = iter.Get();
|
||||
|
||||
PAL_DPWARN(
|
||||
"ClientMem = 0x%p, AllocSize = %8d, MemBlkType = %s, File = %-15s, LineNumber = %8d, AllocNum = %8d",
|
||||
pCurrent->pClientMem,
|
||||
pCurrent->size,
|
||||
MemBlkTypeStr[static_cast<uint32>(pCurrent->blockType)],
|
||||
pCurrent->pFilename,
|
||||
pCurrent->lineNumber,
|
||||
pCurrent->allocNum);
|
||||
}
|
||||
|
||||
PAL_DPWARN("================ End of List ===========================");
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
#endif
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMemTrackerImpl.h
|
||||
* @brief PAL utility collection MemTracker class implementations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PAL_MEMTRACK
|
||||
|
||||
#include "palIntrusiveListImpl.h"
|
||||
#include "palMemTracker.h"
|
||||
#include "palSysMemory.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Table to convert a blockType to a string. Used by the logging routines.
|
||||
constexpr const char* MemBlkTypeStr[] =
|
||||
{
|
||||
"Malloc", ///< MemBlkType::Malloc
|
||||
"New", ///< MemBlkType::New
|
||||
"NewArray", ///< MemBlkType::NewArray
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template <typename Allocator>
|
||||
MemTracker<Allocator>::MemTracker(
|
||||
Allocator*const pAllocator)
|
||||
:
|
||||
m_markerSizeUints(MarkerSizeUints),
|
||||
m_markerSizeBytes(MarkerSizeBytes),
|
||||
m_pAllocator(pAllocator),
|
||||
m_nextAllocNum(1),
|
||||
m_breakOnAllocNum(0)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template <typename Allocator>
|
||||
MemTracker<Allocator>::~MemTracker()
|
||||
{
|
||||
// Clean-up leaked memory if needed
|
||||
if (m_trackerList.IsEmpty() == false)
|
||||
{
|
||||
// If the list isn't empty, we have a leak. The leak could either be caused by an internal PAL leak,
|
||||
// a client leak, or even the application not destroying API objects.
|
||||
PAL_ALERT_ALWAYS();
|
||||
|
||||
// Dump out a list of unfreed blocks.
|
||||
MemoryReport();
|
||||
|
||||
FreeLeakedMemory();
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template <typename Allocator>
|
||||
Result MemTracker<Allocator>::Init()
|
||||
{
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Adds the newly allocated memory block to the list of blocks for tracking.
|
||||
//
|
||||
// The tracking information includes things like filename, line numbers, and type of block. Also, given a pointer,
|
||||
// adds the Underrun/Overrun markers to the memory allocated, and return a pointer to the actual client usable memory.
|
||||
//
|
||||
// See MemTracker::Alloc() which is used to allocate memory that is being tracked.
|
||||
template <typename Allocator>
|
||||
void* MemTracker<Allocator>::AddMemElement(
|
||||
void* pMem, // [in,out] Original pointer allocated by MemTracker::Alloc.
|
||||
size_t bytes, // Client requested allocation size in bytes.
|
||||
size_t align, // The max of the client-requested alignment or the internal alignment, in bytes.
|
||||
MemBlkType blockType, // Block type based on calling allocation routine.
|
||||
const char* pFilename, // Client filename that is requesting the memory.
|
||||
uint32 lineNumber) // Line number in client file that is requesting the memory.
|
||||
{
|
||||
// Our internal data is all relative to the client pointer so find that first. See Alloc for more details.
|
||||
// (align1)(MemTrackerList::Node)(MemTrackerElem)(underflow tracker)(client allocation)(align2)(overflow tracker)
|
||||
constexpr size_t InternalSize = sizeof(MemTrackerList::Node) + sizeof(MemTrackerElem);
|
||||
|
||||
void*const pClientMem = VoidPtrAlign(VoidPtrInc(pMem, m_markerSizeBytes + InternalSize), align);
|
||||
uint32* pUnderrun = static_cast<uint32*>(VoidPtrDec(pClientMem, m_markerSizeBytes));
|
||||
uint32* pOverrun = static_cast<uint32*>(VoidPtrInc(pClientMem, Pow2Align(bytes, sizeof(uint32))));
|
||||
|
||||
auto*const pNewElement = static_cast<MemTrackerElem*>(VoidPtrDec(pUnderrun, sizeof(MemTrackerElem)));
|
||||
void*const pNewNodeMem = VoidPtrDec(pNewElement, sizeof(MemTrackerList::Node));
|
||||
auto*const pNewNode = PAL_PLACEMENT_NEW(pNewNodeMem) MemTrackerList::Node(pNewElement);
|
||||
|
||||
// Mark the memory with the underrun/overrun marker.
|
||||
for (uint32 markerUints = 0; markerUints < m_markerSizeUints; ++markerUints)
|
||||
{
|
||||
*pUnderrun++ = UnderrunSentinel;
|
||||
*pOverrun++ = OverrunSentinel;
|
||||
}
|
||||
|
||||
pNewElement->size = bytes;
|
||||
pNewElement->pFilename = pFilename;
|
||||
pNewElement->lineNumber = lineNumber;
|
||||
pNewElement->blockType = blockType;
|
||||
pNewElement->pClientMem = pClientMem;
|
||||
pNewElement->pOrigMem = pMem;
|
||||
pNewElement->pList = &m_trackerList;
|
||||
|
||||
MutexAuto lock(&m_mutex);
|
||||
|
||||
// Trigger an assert if we're about to allocate the break-on-allocation number.
|
||||
if (m_nextAllocNum == m_breakOnAllocNum)
|
||||
{
|
||||
PAL_ASSERT_ALWAYS();
|
||||
}
|
||||
|
||||
pNewElement->allocNum = m_nextAllocNum;
|
||||
++m_nextAllocNum;
|
||||
|
||||
m_trackerList.PushFront(pNewNode);
|
||||
|
||||
return pClientMem;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Removes an allocated block from the list of blocks used for tracking.
|
||||
//
|
||||
// The routine checks for invalid frees (and duplicate frees). Also, the routine is able to detect mismatched alloc/free
|
||||
// usage based on the blockType. The routine is called with the pointer to the client usable memory and returns the
|
||||
// pointer to the allocated memory.
|
||||
//
|
||||
// See MemTracker::Free() which is used to free memory that is being tracked.
|
||||
template <typename Allocator>
|
||||
void* MemTracker<Allocator>::RemoveMemElement(
|
||||
void* pClientMem, // Pointer to client usable memory.
|
||||
MemBlkType blockType) // Block type based on calling deallocation routine.
|
||||
{
|
||||
void* pOrigPtr = nullptr;
|
||||
|
||||
// Recall that this is our internal memory layout. See Alloc for more details.
|
||||
// (align1)(MemTrackerList::Node)(MemTrackerElem)(underflow tracker)(client allocation)(align2)(overflow tracker)
|
||||
uint32* pUnderrun = static_cast<uint32*>(VoidPtrDec(pClientMem, m_markerSizeBytes));
|
||||
auto*const pCurrent = static_cast<MemTrackerElem*>(VoidPtrDec(pUnderrun, sizeof(MemTrackerElem)));
|
||||
auto*const pCurrentNode = static_cast<MemTrackerList::Node*>(VoidPtrDec(pCurrent, sizeof(MemTrackerList::Node)));
|
||||
uint32* pOverrun = static_cast<uint32*>(VoidPtrInc(pClientMem, Pow2Align(pCurrent->size, sizeof(uint32))));
|
||||
|
||||
// We should not be trying to free something twice or trying to free something which has not been allocated
|
||||
// by this MemTracker. We can verify both of these things by checking that the tracker's pList is equal to the
|
||||
// MemTracker's list.
|
||||
if (pCurrent->pList != &m_trackerList)
|
||||
{
|
||||
// A free was attempted on an unrecognized pointer.
|
||||
PAL_DPERROR("Invalid Free Attempted with ptr = : (%#x)", pClientMem);
|
||||
}
|
||||
else if (pCurrent->blockType != blockType)
|
||||
{
|
||||
// We have a mismatch in the alloc/free pair, e.g. PAL_NEW with PAL_FREE etc. return early here without freeing
|
||||
// the memory so it shows up as a leak.
|
||||
PAL_DPERROR("Trying to Free %s as %s.",
|
||||
MemBlkTypeStr[static_cast<uint32>(pCurrent->blockType)],
|
||||
MemBlkTypeStr[static_cast<uint32>(blockType)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We should check for memory corruption due to overflow or underflow before continuing because any underflow
|
||||
// might indicate that our internal state is corrupted. This could lead to a crash in the code below.
|
||||
for (uint32 markerUints = 0; markerUints < m_markerSizeUints; ++markerUints)
|
||||
{
|
||||
PAL_ASSERT(*pUnderrun++ == UnderrunSentinel);
|
||||
PAL_ASSERT(*pOverrun++ == OverrunSentinel);
|
||||
}
|
||||
|
||||
// Remove our tracker from the list and set it's pList to null to detect a double-free in the future.
|
||||
MutexAuto lock(&m_mutex);
|
||||
|
||||
m_trackerList.Erase(pCurrentNode);
|
||||
|
||||
pCurrent->pList = nullptr;
|
||||
pOrigPtr = pCurrent->pOrigMem;
|
||||
}
|
||||
|
||||
// Return a pointer to the actual allocated block.
|
||||
return pOrigPtr;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Allocates a block of memory and tracks it using the memory tracker.
|
||||
template <typename Allocator>
|
||||
void* MemTracker<Allocator>::Alloc(
|
||||
const AllocInfo& allocInfo)
|
||||
{
|
||||
// Allocating zero bytes of memory results in undefined behavior.
|
||||
PAL_ASSERT(allocInfo.bytes > 0);
|
||||
|
||||
void* pMem = nullptr;
|
||||
|
||||
// We want to allocate extra memory from the caller's allocator, in this layout:
|
||||
// (align1)(MemTrackerList::Node)(MemTrackerElem)(underflow tracker)(client allocation)(align2)(overflow tracker)
|
||||
// Here's why we need each of those sections:
|
||||
// 1. align1 is zero or more bytes needed to align the client allocation and our internal data.
|
||||
// 2. The MemTrackerList::Node object, which is used to link this allocation into m_trackerList.
|
||||
// 3. The MemTrackerElem struct contains bookkeeping data we need to report memory errors.
|
||||
// 4. The underflow and overflow trackers detect out of bounds writes. They are optional.
|
||||
// 5. The client allocation, which is actually returned to the caller.
|
||||
// 6. align2 is zero or more bytes needed to DWORD-align the overflow tracker.
|
||||
constexpr size_t InternalAlignment = Max(alignof(MemTrackerList::Node), alignof(MemTrackerElem));
|
||||
const size_t paddedAlignBytes = Max(allocInfo.alignment, InternalAlignment);
|
||||
const size_t paddedSizeBytes = (paddedAlignBytes + // 1
|
||||
sizeof(MemTrackerList::Node) + // 2
|
||||
sizeof(MemTrackerElem) + // 3
|
||||
m_markerSizeBytes + // 4.a
|
||||
Pow2Align(allocInfo.bytes, sizeof(uint32)) + // 5 & 6
|
||||
m_markerSizeBytes); // 4.b
|
||||
|
||||
const AllocInfo memTrackerInfo(paddedSizeBytes, paddedAlignBytes, allocInfo.zeroMem, allocInfo.allocType,
|
||||
allocInfo.blockType, allocInfo.pFilename, allocInfo.lineNumber);
|
||||
|
||||
pMem = m_pAllocator->Alloc(memTrackerInfo);
|
||||
|
||||
if (pMem != nullptr)
|
||||
{
|
||||
// Don't bother adding a failed allocation to the Memtrack list.
|
||||
pMem = AddMemElement(pMem,
|
||||
allocInfo.bytes,
|
||||
paddedAlignBytes,
|
||||
allocInfo.blockType,
|
||||
allocInfo.pFilename,
|
||||
allocInfo.lineNumber);
|
||||
}
|
||||
|
||||
return pMem;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Frees a block of memory. The routine is called with the pointer to the client usable memory.
|
||||
//
|
||||
// See MemTracker::RemoveMemElement() which is used to validate the free.
|
||||
template <typename Allocator>
|
||||
void MemTracker<Allocator>::Free(
|
||||
const FreeInfo& freeInfo)
|
||||
{
|
||||
// Don't want to call RemoveMemElement if the ptr is null.
|
||||
if (freeInfo.pClientMem != nullptr)
|
||||
{
|
||||
void* pMem = RemoveMemElement(freeInfo.pClientMem, freeInfo.blockType);
|
||||
|
||||
// If this free call is valid (RemoveMemElement doesn't return nullptr), release the memory.
|
||||
if (pMem != nullptr)
|
||||
{
|
||||
m_pAllocator->Free(FreeInfo(pMem, freeInfo.blockType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Frees all memory that has not been explicitly freed (in other words, memory that has leaked). This function is only
|
||||
// expected to be called when the memory tracker is being destroyed.
|
||||
template <typename Allocator>
|
||||
void MemTracker<Allocator>::FreeLeakedMemory()
|
||||
{
|
||||
for (MemTrackerList::Iter iter = m_trackerList.Begin(); iter.IsValid(); )
|
||||
{
|
||||
MemTrackerElem*const pCurrent = iter.Get();
|
||||
|
||||
// Free will release the memory for tracking and the actual element. This will invalidate our list iterator
|
||||
// unless we advance the iterator first.
|
||||
iter.Next();
|
||||
|
||||
Free(FreeInfo(pCurrent->pClientMem, pCurrent->blockType));
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Outputs information about leaked memory by traversing the memory tracker list.
|
||||
template <typename Allocator>
|
||||
void MemTracker<Allocator>::MemoryReport()
|
||||
{
|
||||
// When this env var is set to non-zero, don't report leaks.
|
||||
// Useful for crashing apps that don't give us a chance to clean up.
|
||||
const char* pToggle = getenv("AMDPAL_NO_LEAK_REPORT");
|
||||
|
||||
if ((pToggle == nullptr) || (atoi(pToggle) == 0))
|
||||
{
|
||||
PAL_DPWARN("================ List of Leaked Blocks ================");
|
||||
|
||||
for (MemTrackerList::Iter iter = m_trackerList.Begin(); iter.IsValid(); iter.Next())
|
||||
{
|
||||
MemTrackerElem*const pCurrent = iter.Get();
|
||||
|
||||
PAL_DPWARN(
|
||||
"ClientMem = 0x%p, AllocSize = %8d, MemBlkType = %s, File = %-15s, LineNumber = %8d, AllocNum = %8d",
|
||||
pCurrent->pClientMem,
|
||||
pCurrent->size,
|
||||
MemBlkTypeStr[static_cast<uint32>(pCurrent->blockType)],
|
||||
pCurrent->pFilename,
|
||||
pCurrent->lineNumber,
|
||||
pCurrent->allocNum);
|
||||
}
|
||||
|
||||
PAL_DPWARN("================ End of List ===========================");
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,398 +1,398 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMutex.h
|
||||
* @brief PAL utility collection Mutex and MutexAuto class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palAssert.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
// WIN32_NO_STATUS prevents winnt.h from re-defining NTSTATUS macros that cause build warnings if
|
||||
// ntstatus.h is also included.
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Platform-agnostic mutex primitive.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
/// Defines MutexData as a Windows CRITICAL_SECTION
|
||||
typedef CRITICAL_SECTION MutexData;
|
||||
Mutex() noexcept : m_osMutex {} { InitializeCriticalSection(&m_osMutex); }
|
||||
~Mutex() { DeleteCriticalSection(&m_osMutex); };
|
||||
#else
|
||||
/// Defines MutexData as a unix pthread_mutex_t
|
||||
typedef pthread_mutex_t MutexData;
|
||||
Mutex() noexcept : m_osMutex {} { pthread_mutex_init(&m_osMutex, nullptr); }
|
||||
~Mutex() { pthread_mutex_destroy(&m_osMutex); };
|
||||
#endif
|
||||
|
||||
/// Enters the critical section if it is not contended. If it is contended, wait for the critical section to become
|
||||
/// available, then enter it.
|
||||
void Lock();
|
||||
|
||||
/// Enters the critical section if it is not contended. Does not wait for the critical section to become available
|
||||
/// if it is contended.
|
||||
///
|
||||
/// @returns True if the critical section was entered, false otherwise.
|
||||
bool TryLock();
|
||||
|
||||
/// Leaves the critical section.
|
||||
void Unlock();
|
||||
|
||||
/// Returns the OS specific mutex data.
|
||||
MutexData* GetMutexData() { return &m_osMutex; }
|
||||
|
||||
private:
|
||||
MutexData m_osMutex; ///< Opaque structure to the OS-specific Mutex data
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Mutex);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A "resource acquisition is initialization" (RAII) wrapper for the Mutex class.
|
||||
*
|
||||
* The RAII paradigm allows critical sections to be automatically acquired during this class' constructor, and
|
||||
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
||||
* this class for stack-allocated objects.
|
||||
*
|
||||
* This object will ensure that anything between when the object is allocated on the stack and when it goes out of scope
|
||||
* will be protected from access by multiple threads. See the below example.
|
||||
*
|
||||
* [Code not protected]
|
||||
* {
|
||||
* [Code not protected]
|
||||
* MutexAuto lock(pPtrToMutex);
|
||||
* [Code is protected]
|
||||
* }
|
||||
* [Code not protected]
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class MutexAuto
|
||||
{
|
||||
public:
|
||||
/// Locks the given Mutex.
|
||||
explicit MutexAuto(Mutex* pMutex) : m_pMutex(pMutex)
|
||||
{
|
||||
PAL_ASSERT(m_pMutex != nullptr);
|
||||
m_pMutex->Lock();
|
||||
}
|
||||
|
||||
/// Unlocks the Mutex we locked in the constructor.
|
||||
~MutexAuto()
|
||||
{
|
||||
m_pMutex->Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex* const m_pMutex; ///< The Mutex which this object wraps.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(MutexAuto);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(MutexAuto);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Platform-agnostic rw lock primitive.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class RWLock
|
||||
{
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
/// Defines RWLockData as a Windows RWLOCK
|
||||
typedef SRWLOCK RWLockData;
|
||||
RWLock() noexcept : m_osRWLock {} { InitializeSRWLock(&m_osRWLock); }
|
||||
~RWLock() noexcept { /* No Win32 destory function */ };
|
||||
#else
|
||||
/// Defines RWLockData as a unix pthread_rwlock_t
|
||||
typedef pthread_rwlock_t RWLockData;
|
||||
/// @note pthread_rwlock_init will not fail as called
|
||||
RWLock() noexcept : m_osRWLock {} { pthread_rwlock_init(&m_osRWLock, nullptr); }
|
||||
~RWLock() noexcept { pthread_rwlock_destroy(&m_osRWLock); };
|
||||
#endif
|
||||
|
||||
/// Enumerates the lock type of RWLockAuto
|
||||
enum LockType
|
||||
{
|
||||
ReadOnly = 0, ///< Lock in readonly mode, in other words shared mode.
|
||||
ReadWrite ///< Lock in readwrite mode, in other words exclusive mode.
|
||||
};
|
||||
|
||||
/// Acquires a rw lock in shared mode if it is not contended in exclusive mode.
|
||||
/// If it is contended, wait for rw lock to become available, then enter it.
|
||||
void LockForRead();
|
||||
|
||||
/// Acquires a rw lock in exclusive mode if it is not contended.
|
||||
/// If it is contended, wait for rw lock to become available, then enter it.
|
||||
void LockForWrite();
|
||||
|
||||
/// Try to acquires a rw lock in shared mode if it is not contended in exclusive mode.
|
||||
/// Does not wait for the rw lock to become available.
|
||||
/// @returns True if the rw lock was acquired, false otherwise.
|
||||
bool TryLockForRead();
|
||||
|
||||
/// Try to acquires a rw lock in exclusive mode if it is not contended.
|
||||
/// Does not wait for the rw lock to become available.
|
||||
/// @returns True if the rw lock was acquired, false otherwise.
|
||||
bool TryLockForWrite();
|
||||
|
||||
/// Release the rw lock which is previously contended in shared mode.
|
||||
void UnlockForRead();
|
||||
|
||||
/// Release the rw lock which is previously contended in exclusive mode.
|
||||
void UnlockForWrite();
|
||||
|
||||
/// Returns the OS specific RWLOCK data.
|
||||
RWLockData* GetRWLockData() { return &m_osRWLock; }
|
||||
|
||||
private:
|
||||
RWLockData m_osRWLock; ///< Opaque structure to the OS-specific RWLock data
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(RWLock);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A "resource acquisition is initialization" (RAII) wrapper for the RWLock class.
|
||||
*
|
||||
* The RAII paradigm allows rw lcok to be automatically acquired during this class' constructor, and
|
||||
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
||||
* this class for stack-allocated objects.
|
||||
*
|
||||
* This object will ensure that anything between when the object is allocated on the stack and when it goes out of scope
|
||||
* will be protected from access by multiple threads. See the below example.
|
||||
*
|
||||
* [Code not protected]
|
||||
* {
|
||||
* [Code not protected]
|
||||
* RWLockAuto lock(pPtrToMutex, type);
|
||||
* [Code is protected]
|
||||
* }
|
||||
* [Code not protected]
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <RWLock::LockType type>
|
||||
class RWLockAuto
|
||||
{
|
||||
public:
|
||||
/// Locks the given RWLock.
|
||||
explicit RWLockAuto(RWLock* pRWLock) : m_pRWLock(pRWLock)
|
||||
{
|
||||
PAL_ASSERT(m_pRWLock != nullptr);
|
||||
if (type == RWLock::ReadOnly)
|
||||
{
|
||||
m_pRWLock->LockForRead();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pRWLock->LockForWrite();
|
||||
}
|
||||
}
|
||||
|
||||
/// Unlocks the RWLock we locked in the constructor.
|
||||
~RWLockAuto()
|
||||
{
|
||||
if (type == RWLock::ReadOnly)
|
||||
{
|
||||
m_pRWLock->UnlockForRead();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pRWLock->UnlockForWrite();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RWLock* const m_pRWLock; ///< The RWLock which this object wraps.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(RWLockAuto);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(RWLockAuto);
|
||||
};
|
||||
|
||||
/// Yields the current thread to another thread in the ready state (if available).
|
||||
extern void YieldThread();
|
||||
|
||||
/// Atomic write of 64-bit unsigned integer, using a relaxed memory ordering policy.
|
||||
/// If you need to synchronize more than just pTarget, you may need a new function.
|
||||
///
|
||||
/// @param [in] pTarget Pointer to the value to be read.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern void AtomicWriteRelaxed64(volatile uint64* pTarget, uint64 newValue);
|
||||
|
||||
/// Atomic read of 64-bit unsigned integer, using a relaxed memory ordering policy.
|
||||
/// If you need to synchronize more than just pTarget, you may need a new function.
|
||||
///
|
||||
/// @param [in] pTarget Pointer to the value to be read.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint64 AtomicReadRelaxed64(const volatile uint64* pTarget);
|
||||
|
||||
/// Atomically increments the specified 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pValue Pointer to the value to be incremented.
|
||||
///
|
||||
/// @returns Result of the increment operation.
|
||||
extern uint32 AtomicIncrement(volatile uint32* pValue);
|
||||
|
||||
/// Atomically increment a 64-bit-unsigned integer
|
||||
///
|
||||
/// @param [in,out] pAddend Pointer to the value to be incremented
|
||||
///
|
||||
/// @returns Result of the increment operation.
|
||||
extern uint64 AtomicIncrement64(volatile uint64* pAddend);
|
||||
|
||||
/// Atomically decrements the specified 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pValue Pointer to the value to be decremented.
|
||||
///
|
||||
/// @returns Result of the decrement operation.
|
||||
extern uint32 AtomicDecrement(volatile uint32* pValue);
|
||||
|
||||
/// Atomically decrements the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pValue Pointer to the value to be decremented.
|
||||
///
|
||||
/// @returns Result of the decrement operation.
|
||||
extern uint32 AtomicDecrement64(volatile uint64* pValue);
|
||||
|
||||
/// Performs an atomic compare and swap operation on two 32-bit unsigned integers. This operation compares *pTarget
|
||||
/// with oldValue and replaces it with newValue if they match. If the values don't match, no action is taken.
|
||||
/// The original value of *pTarget is returned as a result.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
||||
/// @param [in] oldValue Value to compare *pTarget to.
|
||||
/// @param [in] newValue Value to replace *pTarget with if *pTarget matches oldValue.
|
||||
///
|
||||
/// @returns Previous value at *pTarget.
|
||||
extern uint32 AtomicCompareAndSwap(volatile uint32* pTarget, uint32 oldValue, uint32 newValue);
|
||||
|
||||
/// Atomically exchanges a pair of 32-bit unsigned integers.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
||||
/// @param [in] value New value to be stored in *pTarget.
|
||||
///
|
||||
/// @returns Previous value at *pTarget.
|
||||
extern uint32 AtomicExchange(volatile uint32* pTarget, uint32 value);
|
||||
|
||||
/// Atomically exchanges a pair of 64-bit unsigned integers.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
||||
/// @param [in] value New value to be stored in *pTarget.
|
||||
///
|
||||
/// @returns Previous value at *pTarget.
|
||||
extern uint64 AtomicExchange64(volatile uint64* pTarget, uint64 value);
|
||||
|
||||
/// Atomically exchanges a pair of pointers.
|
||||
///
|
||||
/// @param [in,out] ppTarget Pointer to the address to exchange. The function sets the address pointed to by *ppTarget
|
||||
/// to pValue.
|
||||
/// @param [in] pValue New pointer to be stored in *ppTarget.
|
||||
///
|
||||
/// @returns Previous value at *ppTarget.
|
||||
extern void* AtomicExchangePointer(void*volatile* ppTarget, void* pValue);
|
||||
|
||||
/// Performs an atomic compare and swap operation on a pair of pointers. This operation compares *ppTarget
|
||||
/// with pOldValue and replaces it with pNewValue if they match. If the values don't match, no action is taken.
|
||||
/// The original value of *ppTarget is returned as a result.
|
||||
///
|
||||
/// @param [in,out] ppTarget Pointer to the destination value of the operation.
|
||||
|
||||
/// @param [in] pOldValue Old pointer to compare *ppTarget to.
|
||||
/// @param [in] pNewValue New pointer to replace *ppTarget with if *ppTarget matches pOldValue.
|
||||
///
|
||||
/// @returns Previous value at *ppTarget.
|
||||
extern void* AtomicCompareExchangePointer(void*volatile* ppTarget, void* pOldValue, void* pNewValue);
|
||||
|
||||
/// Atomically add a value to the specific 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pAddend Pointer to the value to be modified.
|
||||
/// @param [in] value Value to add to *pAddend.
|
||||
///
|
||||
/// @returns Result of the add operation.
|
||||
extern uint32 AtomicAdd(volatile uint32* pAddend, uint32 value);
|
||||
|
||||
/// Atomically add a value to the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pAddend Pointer to the value to be modified.
|
||||
/// @param [in] value Value to add to *pAddend.
|
||||
///
|
||||
/// @returns Result of the add operation.
|
||||
extern uint64 AtomicAdd64(volatile uint64* pAddend, uint64 value);
|
||||
|
||||
/// Atomically OR a value to the specific 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to OR to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint32 AtomicOr(volatile uint32* pTarget, uint32 value);
|
||||
|
||||
/// Atomically OR a value to the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to OR to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint64 AtomicOr64(volatile uint64* pTarget, uint64 value);
|
||||
|
||||
/// Atomically AND a value to the specific 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to AND to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint32 AtomicAnd(volatile uint32* pTarget, uint32 value);
|
||||
|
||||
/// Atomically AND a value to the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to AND to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint64 AtomicAnd64(volatile uint64* pTarget, uint64 value);
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palMutex.h
|
||||
* @brief PAL utility collection Mutex and MutexAuto class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palAssert.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
// WIN32_NO_STATUS prevents winnt.h from re-defining NTSTATUS macros that cause build warnings if
|
||||
// ntstatus.h is also included.
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Platform-agnostic mutex primitive.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
/// Defines MutexData as a Windows CRITICAL_SECTION
|
||||
typedef CRITICAL_SECTION MutexData;
|
||||
Mutex() noexcept : m_osMutex {} { InitializeCriticalSection(&m_osMutex); }
|
||||
~Mutex() { DeleteCriticalSection(&m_osMutex); };
|
||||
#else
|
||||
/// Defines MutexData as a unix pthread_mutex_t
|
||||
typedef pthread_mutex_t MutexData;
|
||||
Mutex() noexcept : m_osMutex {} { pthread_mutex_init(&m_osMutex, nullptr); }
|
||||
~Mutex() { pthread_mutex_destroy(&m_osMutex); };
|
||||
#endif
|
||||
|
||||
/// Enters the critical section if it is not contended. If it is contended, wait for the critical section to become
|
||||
/// available, then enter it.
|
||||
void Lock();
|
||||
|
||||
/// Enters the critical section if it is not contended. Does not wait for the critical section to become available
|
||||
/// if it is contended.
|
||||
///
|
||||
/// @returns True if the critical section was entered, false otherwise.
|
||||
bool TryLock();
|
||||
|
||||
/// Leaves the critical section.
|
||||
void Unlock();
|
||||
|
||||
/// Returns the OS specific mutex data.
|
||||
MutexData* GetMutexData() { return &m_osMutex; }
|
||||
|
||||
private:
|
||||
MutexData m_osMutex; ///< Opaque structure to the OS-specific Mutex data
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Mutex);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A "resource acquisition is initialization" (RAII) wrapper for the Mutex class.
|
||||
*
|
||||
* The RAII paradigm allows critical sections to be automatically acquired during this class' constructor, and
|
||||
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
||||
* this class for stack-allocated objects.
|
||||
*
|
||||
* This object will ensure that anything between when the object is allocated on the stack and when it goes out of scope
|
||||
* will be protected from access by multiple threads. See the below example.
|
||||
*
|
||||
* [Code not protected]
|
||||
* {
|
||||
* [Code not protected]
|
||||
* MutexAuto lock(pPtrToMutex);
|
||||
* [Code is protected]
|
||||
* }
|
||||
* [Code not protected]
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class MutexAuto
|
||||
{
|
||||
public:
|
||||
/// Locks the given Mutex.
|
||||
explicit MutexAuto(Mutex* pMutex) : m_pMutex(pMutex)
|
||||
{
|
||||
PAL_ASSERT(m_pMutex != nullptr);
|
||||
m_pMutex->Lock();
|
||||
}
|
||||
|
||||
/// Unlocks the Mutex we locked in the constructor.
|
||||
~MutexAuto()
|
||||
{
|
||||
m_pMutex->Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex* const m_pMutex; ///< The Mutex which this object wraps.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(MutexAuto);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(MutexAuto);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Platform-agnostic rw lock primitive.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
class RWLock
|
||||
{
|
||||
public:
|
||||
#if defined(_WIN32)
|
||||
/// Defines RWLockData as a Windows RWLOCK
|
||||
typedef SRWLOCK RWLockData;
|
||||
RWLock() noexcept : m_osRWLock {} { InitializeSRWLock(&m_osRWLock); }
|
||||
~RWLock() noexcept { /* No Win32 destory function */ };
|
||||
#else
|
||||
/// Defines RWLockData as a unix pthread_rwlock_t
|
||||
typedef pthread_rwlock_t RWLockData;
|
||||
/// @note pthread_rwlock_init will not fail as called
|
||||
RWLock() noexcept : m_osRWLock {} { pthread_rwlock_init(&m_osRWLock, nullptr); }
|
||||
~RWLock() noexcept { pthread_rwlock_destroy(&m_osRWLock); };
|
||||
#endif
|
||||
|
||||
/// Enumerates the lock type of RWLockAuto
|
||||
enum LockType
|
||||
{
|
||||
ReadOnly = 0, ///< Lock in readonly mode, in other words shared mode.
|
||||
ReadWrite ///< Lock in readwrite mode, in other words exclusive mode.
|
||||
};
|
||||
|
||||
/// Acquires a rw lock in shared mode if it is not contended in exclusive mode.
|
||||
/// If it is contended, wait for rw lock to become available, then enter it.
|
||||
void LockForRead();
|
||||
|
||||
/// Acquires a rw lock in exclusive mode if it is not contended.
|
||||
/// If it is contended, wait for rw lock to become available, then enter it.
|
||||
void LockForWrite();
|
||||
|
||||
/// Try to acquires a rw lock in shared mode if it is not contended in exclusive mode.
|
||||
/// Does not wait for the rw lock to become available.
|
||||
/// @returns True if the rw lock was acquired, false otherwise.
|
||||
bool TryLockForRead();
|
||||
|
||||
/// Try to acquires a rw lock in exclusive mode if it is not contended.
|
||||
/// Does not wait for the rw lock to become available.
|
||||
/// @returns True if the rw lock was acquired, false otherwise.
|
||||
bool TryLockForWrite();
|
||||
|
||||
/// Release the rw lock which is previously contended in shared mode.
|
||||
void UnlockForRead();
|
||||
|
||||
/// Release the rw lock which is previously contended in exclusive mode.
|
||||
void UnlockForWrite();
|
||||
|
||||
/// Returns the OS specific RWLOCK data.
|
||||
RWLockData* GetRWLockData() { return &m_osRWLock; }
|
||||
|
||||
private:
|
||||
RWLockData m_osRWLock; ///< Opaque structure to the OS-specific RWLock data
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(RWLock);
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief A "resource acquisition is initialization" (RAII) wrapper for the RWLock class.
|
||||
*
|
||||
* The RAII paradigm allows rw lcok to be automatically acquired during this class' constructor, and
|
||||
* automatically released when a stack-allocated wrapper object goes out-of-scope. As such, it only makes sense to use
|
||||
* this class for stack-allocated objects.
|
||||
*
|
||||
* This object will ensure that anything between when the object is allocated on the stack and when it goes out of scope
|
||||
* will be protected from access by multiple threads. See the below example.
|
||||
*
|
||||
* [Code not protected]
|
||||
* {
|
||||
* [Code not protected]
|
||||
* RWLockAuto lock(pPtrToMutex, type);
|
||||
* [Code is protected]
|
||||
* }
|
||||
* [Code not protected]
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template <RWLock::LockType type>
|
||||
class RWLockAuto
|
||||
{
|
||||
public:
|
||||
/// Locks the given RWLock.
|
||||
explicit RWLockAuto(RWLock* pRWLock) : m_pRWLock(pRWLock)
|
||||
{
|
||||
PAL_ASSERT(m_pRWLock != nullptr);
|
||||
if (type == RWLock::ReadOnly)
|
||||
{
|
||||
m_pRWLock->LockForRead();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pRWLock->LockForWrite();
|
||||
}
|
||||
}
|
||||
|
||||
/// Unlocks the RWLock we locked in the constructor.
|
||||
~RWLockAuto()
|
||||
{
|
||||
if (type == RWLock::ReadOnly)
|
||||
{
|
||||
m_pRWLock->UnlockForRead();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pRWLock->UnlockForWrite();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RWLock* const m_pRWLock; ///< The RWLock which this object wraps.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(RWLockAuto);
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(RWLockAuto);
|
||||
};
|
||||
|
||||
/// Yields the current thread to another thread in the ready state (if available).
|
||||
extern void YieldThread();
|
||||
|
||||
/// Atomic write of 64-bit unsigned integer, using a relaxed memory ordering policy.
|
||||
/// If you need to synchronize more than just pTarget, you may need a new function.
|
||||
///
|
||||
/// @param [in] pTarget Pointer to the value to be read.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern void AtomicWriteRelaxed64(volatile uint64* pTarget, uint64 newValue);
|
||||
|
||||
/// Atomic read of 64-bit unsigned integer, using a relaxed memory ordering policy.
|
||||
/// If you need to synchronize more than just pTarget, you may need a new function.
|
||||
///
|
||||
/// @param [in] pTarget Pointer to the value to be read.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint64 AtomicReadRelaxed64(const volatile uint64* pTarget);
|
||||
|
||||
/// Atomically increments the specified 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pValue Pointer to the value to be incremented.
|
||||
///
|
||||
/// @returns Result of the increment operation.
|
||||
extern uint32 AtomicIncrement(volatile uint32* pValue);
|
||||
|
||||
/// Atomically increment a 64-bit-unsigned integer
|
||||
///
|
||||
/// @param [in,out] pAddend Pointer to the value to be incremented
|
||||
///
|
||||
/// @returns Result of the increment operation.
|
||||
extern uint64 AtomicIncrement64(volatile uint64* pAddend);
|
||||
|
||||
/// Atomically decrements the specified 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pValue Pointer to the value to be decremented.
|
||||
///
|
||||
/// @returns Result of the decrement operation.
|
||||
extern uint32 AtomicDecrement(volatile uint32* pValue);
|
||||
|
||||
/// Atomically decrements the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pValue Pointer to the value to be decremented.
|
||||
///
|
||||
/// @returns Result of the decrement operation.
|
||||
extern uint32 AtomicDecrement64(volatile uint64* pValue);
|
||||
|
||||
/// Performs an atomic compare and swap operation on two 32-bit unsigned integers. This operation compares *pTarget
|
||||
/// with oldValue and replaces it with newValue if they match. If the values don't match, no action is taken.
|
||||
/// The original value of *pTarget is returned as a result.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
||||
/// @param [in] oldValue Value to compare *pTarget to.
|
||||
/// @param [in] newValue Value to replace *pTarget with if *pTarget matches oldValue.
|
||||
///
|
||||
/// @returns Previous value at *pTarget.
|
||||
extern uint32 AtomicCompareAndSwap(volatile uint32* pTarget, uint32 oldValue, uint32 newValue);
|
||||
|
||||
/// Atomically exchanges a pair of 32-bit unsigned integers.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
||||
/// @param [in] value New value to be stored in *pTarget.
|
||||
///
|
||||
/// @returns Previous value at *pTarget.
|
||||
extern uint32 AtomicExchange(volatile uint32* pTarget, uint32 value);
|
||||
|
||||
/// Atomically exchanges a pair of 64-bit unsigned integers.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the destination value of the operation.
|
||||
/// @param [in] value New value to be stored in *pTarget.
|
||||
///
|
||||
/// @returns Previous value at *pTarget.
|
||||
extern uint64 AtomicExchange64(volatile uint64* pTarget, uint64 value);
|
||||
|
||||
/// Atomically exchanges a pair of pointers.
|
||||
///
|
||||
/// @param [in,out] ppTarget Pointer to the address to exchange. The function sets the address pointed to by *ppTarget
|
||||
/// to pValue.
|
||||
/// @param [in] pValue New pointer to be stored in *ppTarget.
|
||||
///
|
||||
/// @returns Previous value at *ppTarget.
|
||||
extern void* AtomicExchangePointer(void*volatile* ppTarget, void* pValue);
|
||||
|
||||
/// Performs an atomic compare and swap operation on a pair of pointers. This operation compares *ppTarget
|
||||
/// with pOldValue and replaces it with pNewValue if they match. If the values don't match, no action is taken.
|
||||
/// The original value of *ppTarget is returned as a result.
|
||||
///
|
||||
/// @param [in,out] ppTarget Pointer to the destination value of the operation.
|
||||
|
||||
/// @param [in] pOldValue Old pointer to compare *ppTarget to.
|
||||
/// @param [in] pNewValue New pointer to replace *ppTarget with if *ppTarget matches pOldValue.
|
||||
///
|
||||
/// @returns Previous value at *ppTarget.
|
||||
extern void* AtomicCompareExchangePointer(void*volatile* ppTarget, void* pOldValue, void* pNewValue);
|
||||
|
||||
/// Atomically add a value to the specific 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pAddend Pointer to the value to be modified.
|
||||
/// @param [in] value Value to add to *pAddend.
|
||||
///
|
||||
/// @returns Result of the add operation.
|
||||
extern uint32 AtomicAdd(volatile uint32* pAddend, uint32 value);
|
||||
|
||||
/// Atomically add a value to the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pAddend Pointer to the value to be modified.
|
||||
/// @param [in] value Value to add to *pAddend.
|
||||
///
|
||||
/// @returns Result of the add operation.
|
||||
extern uint64 AtomicAdd64(volatile uint64* pAddend, uint64 value);
|
||||
|
||||
/// Atomically OR a value to the specific 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to OR to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint32 AtomicOr(volatile uint32* pTarget, uint32 value);
|
||||
|
||||
/// Atomically OR a value to the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to OR to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint64 AtomicOr64(volatile uint64* pTarget, uint64 value);
|
||||
|
||||
/// Atomically AND a value to the specific 32-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to AND to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint32 AtomicAnd(volatile uint32* pTarget, uint32 value);
|
||||
|
||||
/// Atomically AND a value to the specified 64-bit unsigned integer.
|
||||
///
|
||||
/// @param [in,out] pTarget Pointer to the value to be modified.
|
||||
/// @param [in] value Value to AND to *pTarget.
|
||||
///
|
||||
/// @returns The original value of *pTarget.
|
||||
extern uint64 AtomicAnd64(volatile uint64* pTarget, uint64 value);
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,447 +1,447 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palSpan.h
|
||||
* @brief PAL utility collection Span class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palAssert.h"
|
||||
#include "palSysMemory.h"
|
||||
#include "palInlineFuncs.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Span container
|
||||
*
|
||||
* Span is an array with a length, where the data is not owned by the Span object. It is similar to C++20 std::span,
|
||||
* but only the dynamic extent variant. It is similar to LLVM MutableArrayRef and ArrayRef. A Span is intended to
|
||||
* be passed around by value.
|
||||
*
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class Span
|
||||
{
|
||||
public:
|
||||
/// Constructor from nothing. This allows you to use {} to mean an empty Span.
|
||||
constexpr Span() : m_pData(nullptr), m_numElements(0) {}
|
||||
|
||||
/// Constructor from pointer and length
|
||||
///
|
||||
/// @param [in] data Pointer to the start of the array
|
||||
/// @param numElements Number of elements in the array
|
||||
constexpr Span(T* pData, size_t numElements) : m_pData(pData), m_numElements(numElements) {}
|
||||
|
||||
/// Copy constructor
|
||||
///
|
||||
/// @param [in] src Other Span to copy from
|
||||
constexpr Span(const Span<T>& src) : m_pData(src.m_pData), m_numElements(src.m_numElements) {}
|
||||
|
||||
/// Constructor from C++ array
|
||||
///
|
||||
/// @param [in] src C++ array
|
||||
template<size_t NumElements> constexpr Span(T(& src)[NumElements]) : m_pData(&src[0]), m_numElements(NumElements) {}
|
||||
|
||||
/// Constructor from single element
|
||||
///
|
||||
/// @param [in] src Single element
|
||||
constexpr Span(T& src) : m_pData(&src), m_numElements(1) {}
|
||||
|
||||
/// Implicitly convert a Span to its const-element equivalent.
|
||||
///
|
||||
/// @returns The same span, but with const element type
|
||||
constexpr operator Span<const T>() const { return Span<const T>(m_pData, m_numElements); }
|
||||
|
||||
/// Assignment operator
|
||||
///
|
||||
/// @param [in] src Other Span to copy from
|
||||
constexpr Span<T>& operator=(const Span<T>& src) {
|
||||
m_pData = src.m_pData;
|
||||
m_numElements = src.m_numElements;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
constexpr T& At(size_t index) const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(index < m_numElements);
|
||||
return *(m_pData + index);
|
||||
}
|
||||
|
||||
constexpr T& operator[](size_t index) const noexcept { return At(index); }
|
||||
///@}
|
||||
|
||||
/// Returns the data at the front of the vector.
|
||||
///
|
||||
/// @returns The data at the front of the vector.
|
||||
constexpr T& Front() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return *m_pData;
|
||||
}
|
||||
|
||||
/// Returns the data at the back of the vector.
|
||||
///
|
||||
/// @returns The data at the back of the vector.
|
||||
constexpr T& Back() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return *(m_pData + (m_numElements - 1));
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first element of the vector.
|
||||
///
|
||||
/// @returns An iterator to first element of the vector.
|
||||
constexpr T* Begin() const { return m_pData; }
|
||||
|
||||
/// Returns an iterator beyond the last element of the vector. (NOT at the last element like Util::Vector::End()!)
|
||||
///
|
||||
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns VectorIterator An iterator to last element of the vector.
|
||||
constexpr T* End() const { return m_pData + m_numElements; }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage.
|
||||
/// For a non-empty span, the returned pointer contains address of the first element.
|
||||
/// For an empty span, the returned pointer may or may not be a null pointer.
|
||||
constexpr T* Data() const { return m_pData; }
|
||||
|
||||
/// Returns the extent of the span.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the number of elements currently present in the span.
|
||||
constexpr size_t NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns the size in bytes the Span represents.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the size in bytes the entire span represents.
|
||||
constexpr size_t SizeInBytes() const { return ElementSize() * m_numElements; }
|
||||
|
||||
/// Returns true if the number of elements present in the vector is equal to zero.
|
||||
///
|
||||
/// @returns True if the span is empty.
|
||||
constexpr bool IsEmpty() const { return (m_numElements == 0); }
|
||||
|
||||
/// Returns a "subspan", a view over a subset range of the elements.
|
||||
///
|
||||
/// @warning Behavior is undefined if either
|
||||
/// - offset is greater than NumElements(), or
|
||||
/// - count is not size_t(-1) and is greater than NumElements()-offset.
|
||||
///
|
||||
/// Note that size_t(-1) is equivalent to C++20 std::dynamic_extent, which the C++20 std::span::subspan uses
|
||||
/// in the same way to mean "take the remainder of the elements from offset".
|
||||
///
|
||||
/// @param offset Zero-based offset to start the subspan at
|
||||
/// @param count Number of elements in the subspan, or size_t(-1) for the remainder of the elements from offset
|
||||
///
|
||||
/// @returns The subspan
|
||||
constexpr Span Subspan(
|
||||
size_t offset,
|
||||
size_t count) const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((offset <= NumElements())
|
||||
&& ((count == size_t(-1)) || (count <= NumElements() - offset)));
|
||||
if (count == size_t(-1))
|
||||
{
|
||||
count = NumElements() - offset;
|
||||
}
|
||||
return Span(Data() + offset, count);
|
||||
}
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the front.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the front
|
||||
///
|
||||
/// @returns The subspan
|
||||
constexpr Span DropFront(
|
||||
size_t count = 1) const
|
||||
{
|
||||
Span retVal;
|
||||
if (count < NumElements())
|
||||
{
|
||||
retVal = Subspan(count, size_t(-1));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the back.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the back
|
||||
///
|
||||
/// @returns The subspan
|
||||
constexpr Span DropBack(
|
||||
size_t count = 1) const
|
||||
{
|
||||
Span retVal;
|
||||
if (count < NumElements())
|
||||
{
|
||||
retVal = Subspan(0, NumElements() - count);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using T* as `iterator` and 32-bit size and difference types
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by c++ language features such as `range for`.
|
||||
/// These should not be called directly as they do not adhere to PAL coding standards.
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using iterator = T*;
|
||||
using difference_type = size_t;
|
||||
using size_type = size_t;
|
||||
|
||||
constexpr iterator begin() const noexcept { return m_pData; }
|
||||
constexpr iterator end() const noexcept { return (m_pData + m_numElements); }
|
||||
constexpr bool empty() const noexcept { return IsEmpty(); }
|
||||
constexpr size_type size() const noexcept { return m_numElements; }
|
||||
///@}
|
||||
|
||||
protected:
|
||||
template<typename U = T, typename R = std::conditional_t<std::is_void_v<U>, char, U>>
|
||||
static constexpr size_t ElementSize() { return sizeof(R); }
|
||||
|
||||
template<typename R = T, bool Condition = true>
|
||||
using IfConst = std::enable_if_t<std::is_const_v<R> == Condition>;
|
||||
|
||||
template<typename R = T, bool Condition = true>
|
||||
using IfPtr = std::enable_if_t<std::is_pointer_v<R> == Condition>;
|
||||
|
||||
T* m_pData; // Pointer to the current data.
|
||||
size_t m_numElements; // Number of elements present.
|
||||
};
|
||||
|
||||
/// ====================================================================================================================
|
||||
/// Span template specialization for const void byte buffers.
|
||||
template<>
|
||||
class Span<const void> : public Span<const char>
|
||||
{
|
||||
using Byte = const char;
|
||||
using Base = Span<Byte>;
|
||||
|
||||
public:
|
||||
/// Constructor from nothing. This allows you to use {} to mean an empty Span.
|
||||
constexpr Span() : Base() {}
|
||||
|
||||
/// Template constructor from any pointer and length
|
||||
///
|
||||
/// @param [in] data Pointer to the start of the buffer or array
|
||||
/// @param numElements Number of bytes or elements in the buffer or array
|
||||
template<typename T>
|
||||
Span(const T* pData, size_t numElements) : Base(reinterpret_cast<Byte*>(pData), ElementSize<T>() * numElements) {}
|
||||
|
||||
/// Template copy constructor
|
||||
///
|
||||
/// @param [in] src Other Span<T> to copy from
|
||||
template<typename T = const void>
|
||||
Span(const Span<T>& src) : Span(src.Data(), src.NumElements()) {}
|
||||
|
||||
/// Template constructor from any C++ array
|
||||
///
|
||||
/// @param [in] src C++ array
|
||||
template<typename T, size_t NumElements>
|
||||
Span(const T(& src)[NumElements]) : Span(&src[0], NumElements) {}
|
||||
|
||||
/// Constructor from any single element
|
||||
///
|
||||
/// @param [in] src Single element
|
||||
template<typename T, typename Enabled = IfPtr<T, false>>
|
||||
Span(const T& src) : Span(&src, 1) {}
|
||||
|
||||
/// Templated conversion of this typeless Span to a typed Subspan
|
||||
///
|
||||
/// @returns A subspan with typed elemeents and NumElements truncated down to the nearest sizeof(type)
|
||||
template<typename T>
|
||||
operator Span<const T>() const
|
||||
{ return Span<const T>(static_cast<const T*>(Data()), SizeInBytes() / ElementSize<T>()); }
|
||||
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
template<typename T = Byte>
|
||||
const T& At(size_t index) const { return reinterpret_cast<const T&>(Base::At(index)); }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage.
|
||||
/// For a non-empty span, the returned pointer contains address of the first element.
|
||||
/// For an empty span, the returned pointer may or may not be a null pointer.
|
||||
template<typename T = void>
|
||||
constexpr const T* Data() const { return reinterpret_cast<const T*>(Base::Data()); }
|
||||
|
||||
/// Returns a "subspan", a view over a subset range of the elements.
|
||||
///
|
||||
/// @warning Behavior is undefined if either
|
||||
/// - offset is greater than NumElements(), or
|
||||
/// - count is not size_t(-1) and is greater than NumElements()-offset.
|
||||
///
|
||||
/// Note that size_t(-1) is equivalent to C++20 std::dynamic_extent, which the C++20 std::span::subspan uses
|
||||
/// in the same way to mean "take the remainder of the elements from offset".
|
||||
///
|
||||
/// @param offset Zero-based offset to start the subspan at
|
||||
/// @param count Number of elements in the subspan, or size_t(-1) for the remainder of the elements from offset
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<const T> Subspan(size_t offset, size_t count) const { return Span<const void>(Base::Subspan(offset, count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the front.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the front
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<const T> DropFront(size_t count = 1) const { return Span<const void>(Base::DropFront(count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the back.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the back
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<const T> DropBack(size_t count = 1) const { return Span<const void>(Base::DropBack(count)); }
|
||||
};
|
||||
|
||||
/// ====================================================================================================================
|
||||
/// Span template specialization for mutable void byte buffers.
|
||||
template<>
|
||||
class Span<void> : public Span<char>
|
||||
{
|
||||
using Byte = char;
|
||||
using Base = Span<Byte>;
|
||||
|
||||
public:
|
||||
/// Constructor from nothing. This allows you to use {} to mean an empty Span.
|
||||
constexpr Span() : Base() {}
|
||||
|
||||
/// Template constructor from any pointer and length
|
||||
///
|
||||
/// @param [in] data Pointer to the start of the buffer or array
|
||||
/// @param sizeInBytes,numElements Number of bytes or elements in the buffer or array
|
||||
template<typename T, typename Enabled = IfConst<T, false>>
|
||||
Span(T* pData, size_t numElements) : Base(reinterpret_cast<Byte*>(pData), ElementSize<T>() * numElements) { }
|
||||
|
||||
/// Template copy constructor
|
||||
///
|
||||
/// @param [in] src Other Span<T> to copy from
|
||||
template<typename T = void, typename Enabled = IfConst<T, false>>
|
||||
Span(const Span<T>& src) : Span(src.Data(), src.NumElements()) {}
|
||||
|
||||
/// Template constructor from any C++ array
|
||||
///
|
||||
/// @param [in] src C++ array
|
||||
template<typename T, size_t NumElements, typename Enabled = IfConst<T, false>>
|
||||
Span(T(& src)[NumElements]) : Span(&src[0], NumElements) {}
|
||||
|
||||
/// Constructor from any single element
|
||||
///
|
||||
/// @param [in] src Single element
|
||||
template<typename T, typename Enabled = IfConst<T, false>, typename = IfPtr<T, false>>
|
||||
Span(T& src) : Span(&src, 1) {}
|
||||
|
||||
/// Templated conversion of this typeless Span to a typed Subspan
|
||||
///
|
||||
/// @returns A subspan with typed elemeents and NumElements truncated down to the nearest sizeof(type)
|
||||
template<typename T>
|
||||
operator Span<T>() const { return Span<T>(static_cast<T*>(Data()), SizeInBytes() / ElementSize<T>()); }
|
||||
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
template<typename T = Byte>
|
||||
T& At(size_t index) const { return reinterpret_cast<T&>(Base::At(index)); }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage.
|
||||
/// For a non-empty span, the returned pointer contains address of the first element.
|
||||
/// For an empty span, the returned pointer may or may not be a null pointer.
|
||||
template<typename T = void>
|
||||
constexpr T* Data() const { return reinterpret_cast<T*>(Base::Data()); }
|
||||
|
||||
/// Implicitly convert this void Span to its const void equivalent
|
||||
///
|
||||
/// @returns The same span, but of const void type
|
||||
operator Span<const void>() const { return Span<const void>(Data(), NumElements()); }
|
||||
|
||||
/// Returns a "subspan", a view over a subset range of the elements.
|
||||
///
|
||||
/// @warning Behavior is undefined if either
|
||||
/// - offset is greater than NumElements(), or
|
||||
/// - count is not size_t(-1) and is greater than NumElements()-offset.
|
||||
///
|
||||
/// Note that size_t(-1) is equivalent to C++20 std::dynamic_extent, which the C++20 std::span::subspan uses
|
||||
/// in the same way to mean "take the remainder of the elements from offset".
|
||||
///
|
||||
/// @param offset Zero-based offset to start the subspan at
|
||||
/// @param count Number of elements in the subspan, or size_t(-1) for the remainder of the elements from offset
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<T> Subspan(size_t offset, size_t count) const { return Span<void>(Base::Subspan(offset, count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the front.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the front
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<T> DropFront(size_t count = 1) const { return Span<void>(Base::DropFront(count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the back.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the back
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<T> DropBack(size_t count = 1) const { return Span<void>(Base::DropBack(count)); }
|
||||
};
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palSpan.h
|
||||
* @brief PAL utility collection Span class declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palAssert.h"
|
||||
#include "palSysMemory.h"
|
||||
#include "palInlineFuncs.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Span container
|
||||
*
|
||||
* Span is an array with a length, where the data is not owned by the Span object. It is similar to C++20 std::span,
|
||||
* but only the dynamic extent variant. It is similar to LLVM MutableArrayRef and ArrayRef. A Span is intended to
|
||||
* be passed around by value.
|
||||
*
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T>
|
||||
class Span
|
||||
{
|
||||
public:
|
||||
/// Constructor from nothing. This allows you to use {} to mean an empty Span.
|
||||
constexpr Span() : m_pData(nullptr), m_numElements(0) {}
|
||||
|
||||
/// Constructor from pointer and length
|
||||
///
|
||||
/// @param [in] data Pointer to the start of the array
|
||||
/// @param numElements Number of elements in the array
|
||||
constexpr Span(T* pData, size_t numElements) : m_pData(pData), m_numElements(numElements) {}
|
||||
|
||||
/// Copy constructor
|
||||
///
|
||||
/// @param [in] src Other Span to copy from
|
||||
constexpr Span(const Span<T>& src) : m_pData(src.m_pData), m_numElements(src.m_numElements) {}
|
||||
|
||||
/// Constructor from C++ array
|
||||
///
|
||||
/// @param [in] src C++ array
|
||||
template<size_t NumElements> constexpr Span(T(& src)[NumElements]) : m_pData(&src[0]), m_numElements(NumElements) {}
|
||||
|
||||
/// Constructor from single element
|
||||
///
|
||||
/// @param [in] src Single element
|
||||
constexpr Span(T& src) : m_pData(&src), m_numElements(1) {}
|
||||
|
||||
/// Implicitly convert a Span to its const-element equivalent.
|
||||
///
|
||||
/// @returns The same span, but with const element type
|
||||
constexpr operator Span<const T>() const { return Span<const T>(m_pData, m_numElements); }
|
||||
|
||||
/// Assignment operator
|
||||
///
|
||||
/// @param [in] src Other Span to copy from
|
||||
constexpr Span<T>& operator=(const Span<T>& src) {
|
||||
m_pData = src.m_pData;
|
||||
m_numElements = src.m_numElements;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
constexpr T& At(size_t index) const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(index < m_numElements);
|
||||
return *(m_pData + index);
|
||||
}
|
||||
|
||||
constexpr T& operator[](size_t index) const noexcept { return At(index); }
|
||||
///@}
|
||||
|
||||
/// Returns the data at the front of the vector.
|
||||
///
|
||||
/// @returns The data at the front of the vector.
|
||||
constexpr T& Front() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return *m_pData;
|
||||
}
|
||||
|
||||
/// Returns the data at the back of the vector.
|
||||
///
|
||||
/// @returns The data at the back of the vector.
|
||||
constexpr T& Back() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return *(m_pData + (m_numElements - 1));
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first element of the vector.
|
||||
///
|
||||
/// @returns An iterator to first element of the vector.
|
||||
constexpr T* Begin() const { return m_pData; }
|
||||
|
||||
/// Returns an iterator beyond the last element of the vector. (NOT at the last element like Util::Vector::End()!)
|
||||
///
|
||||
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns VectorIterator An iterator to last element of the vector.
|
||||
constexpr T* End() const { return m_pData + m_numElements; }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage.
|
||||
/// For a non-empty span, the returned pointer contains address of the first element.
|
||||
/// For an empty span, the returned pointer may or may not be a null pointer.
|
||||
constexpr T* Data() const { return m_pData; }
|
||||
|
||||
/// Returns the extent of the span.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the number of elements currently present in the span.
|
||||
constexpr size_t NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns the size in bytes the Span represents.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the size in bytes the entire span represents.
|
||||
constexpr size_t SizeInBytes() const { return ElementSize() * m_numElements; }
|
||||
|
||||
/// Returns true if the number of elements present in the vector is equal to zero.
|
||||
///
|
||||
/// @returns True if the span is empty.
|
||||
constexpr bool IsEmpty() const { return (m_numElements == 0); }
|
||||
|
||||
/// Returns a "subspan", a view over a subset range of the elements.
|
||||
///
|
||||
/// @warning Behavior is undefined if either
|
||||
/// - offset is greater than NumElements(), or
|
||||
/// - count is not size_t(-1) and is greater than NumElements()-offset.
|
||||
///
|
||||
/// Note that size_t(-1) is equivalent to C++20 std::dynamic_extent, which the C++20 std::span::subspan uses
|
||||
/// in the same way to mean "take the remainder of the elements from offset".
|
||||
///
|
||||
/// @param offset Zero-based offset to start the subspan at
|
||||
/// @param count Number of elements in the subspan, or size_t(-1) for the remainder of the elements from offset
|
||||
///
|
||||
/// @returns The subspan
|
||||
constexpr Span Subspan(
|
||||
size_t offset,
|
||||
size_t count) const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((offset <= NumElements())
|
||||
&& ((count == size_t(-1)) || (count <= NumElements() - offset)));
|
||||
if (count == size_t(-1))
|
||||
{
|
||||
count = NumElements() - offset;
|
||||
}
|
||||
return Span(Data() + offset, count);
|
||||
}
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the front.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the front
|
||||
///
|
||||
/// @returns The subspan
|
||||
constexpr Span DropFront(
|
||||
size_t count = 1) const
|
||||
{
|
||||
Span retVal;
|
||||
if (count < NumElements())
|
||||
{
|
||||
retVal = Subspan(count, size_t(-1));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the back.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the back
|
||||
///
|
||||
/// @returns The subspan
|
||||
constexpr Span DropBack(
|
||||
size_t count = 1) const
|
||||
{
|
||||
Span retVal;
|
||||
if (count < NumElements())
|
||||
{
|
||||
retVal = Subspan(0, NumElements() - count);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using T* as `iterator` and 32-bit size and difference types
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by c++ language features such as `range for`.
|
||||
/// These should not be called directly as they do not adhere to PAL coding standards.
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using iterator = T*;
|
||||
using difference_type = size_t;
|
||||
using size_type = size_t;
|
||||
|
||||
constexpr iterator begin() const noexcept { return m_pData; }
|
||||
constexpr iterator end() const noexcept { return (m_pData + m_numElements); }
|
||||
constexpr bool empty() const noexcept { return IsEmpty(); }
|
||||
constexpr size_type size() const noexcept { return m_numElements; }
|
||||
///@}
|
||||
|
||||
protected:
|
||||
template<typename U = T, typename R = std::conditional_t<std::is_void_v<U>, char, U>>
|
||||
static constexpr size_t ElementSize() { return sizeof(R); }
|
||||
|
||||
template<typename R = T, bool Condition = true>
|
||||
using IfConst = std::enable_if_t<std::is_const_v<R> == Condition>;
|
||||
|
||||
template<typename R = T, bool Condition = true>
|
||||
using IfPtr = std::enable_if_t<std::is_pointer_v<R> == Condition>;
|
||||
|
||||
T* m_pData; // Pointer to the current data.
|
||||
size_t m_numElements; // Number of elements present.
|
||||
};
|
||||
|
||||
/// ====================================================================================================================
|
||||
/// Span template specialization for const void byte buffers.
|
||||
template<>
|
||||
class Span<const void> : public Span<const char>
|
||||
{
|
||||
using Byte = const char;
|
||||
using Base = Span<Byte>;
|
||||
|
||||
public:
|
||||
/// Constructor from nothing. This allows you to use {} to mean an empty Span.
|
||||
constexpr Span() : Base() {}
|
||||
|
||||
/// Template constructor from any pointer and length
|
||||
///
|
||||
/// @param [in] data Pointer to the start of the buffer or array
|
||||
/// @param numElements Number of bytes or elements in the buffer or array
|
||||
template<typename T>
|
||||
Span(const T* pData, size_t numElements) : Base(reinterpret_cast<Byte*>(pData), ElementSize<T>() * numElements) {}
|
||||
|
||||
/// Template copy constructor
|
||||
///
|
||||
/// @param [in] src Other Span<T> to copy from
|
||||
template<typename T = const void>
|
||||
Span(const Span<T>& src) : Span(src.Data(), src.NumElements()) {}
|
||||
|
||||
/// Template constructor from any C++ array
|
||||
///
|
||||
/// @param [in] src C++ array
|
||||
template<typename T, size_t NumElements>
|
||||
Span(const T(& src)[NumElements]) : Span(&src[0], NumElements) {}
|
||||
|
||||
/// Constructor from any single element
|
||||
///
|
||||
/// @param [in] src Single element
|
||||
template<typename T, typename Enabled = IfPtr<T, false>>
|
||||
Span(const T& src) : Span(&src, 1) {}
|
||||
|
||||
/// Templated conversion of this typeless Span to a typed Subspan
|
||||
///
|
||||
/// @returns A subspan with typed elemeents and NumElements truncated down to the nearest sizeof(type)
|
||||
template<typename T>
|
||||
operator Span<const T>() const
|
||||
{ return Span<const T>(static_cast<const T*>(Data()), SizeInBytes() / ElementSize<T>()); }
|
||||
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
template<typename T = Byte>
|
||||
const T& At(size_t index) const { return reinterpret_cast<const T&>(Base::At(index)); }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage.
|
||||
/// For a non-empty span, the returned pointer contains address of the first element.
|
||||
/// For an empty span, the returned pointer may or may not be a null pointer.
|
||||
template<typename T = void>
|
||||
constexpr const T* Data() const { return reinterpret_cast<const T*>(Base::Data()); }
|
||||
|
||||
/// Returns a "subspan", a view over a subset range of the elements.
|
||||
///
|
||||
/// @warning Behavior is undefined if either
|
||||
/// - offset is greater than NumElements(), or
|
||||
/// - count is not size_t(-1) and is greater than NumElements()-offset.
|
||||
///
|
||||
/// Note that size_t(-1) is equivalent to C++20 std::dynamic_extent, which the C++20 std::span::subspan uses
|
||||
/// in the same way to mean "take the remainder of the elements from offset".
|
||||
///
|
||||
/// @param offset Zero-based offset to start the subspan at
|
||||
/// @param count Number of elements in the subspan, or size_t(-1) for the remainder of the elements from offset
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<const T> Subspan(size_t offset, size_t count) const { return Span<const void>(Base::Subspan(offset, count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the front.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the front
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<const T> DropFront(size_t count = 1) const { return Span<const void>(Base::DropFront(count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the back.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the back
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<const T> DropBack(size_t count = 1) const { return Span<const void>(Base::DropBack(count)); }
|
||||
};
|
||||
|
||||
/// ====================================================================================================================
|
||||
/// Span template specialization for mutable void byte buffers.
|
||||
template<>
|
||||
class Span<void> : public Span<char>
|
||||
{
|
||||
using Byte = char;
|
||||
using Base = Span<Byte>;
|
||||
|
||||
public:
|
||||
/// Constructor from nothing. This allows you to use {} to mean an empty Span.
|
||||
constexpr Span() : Base() {}
|
||||
|
||||
/// Template constructor from any pointer and length
|
||||
///
|
||||
/// @param [in] data Pointer to the start of the buffer or array
|
||||
/// @param sizeInBytes,numElements Number of bytes or elements in the buffer or array
|
||||
template<typename T, typename Enabled = IfConst<T, false>>
|
||||
Span(T* pData, size_t numElements) : Base(reinterpret_cast<Byte*>(pData), ElementSize<T>() * numElements) { }
|
||||
|
||||
/// Template copy constructor
|
||||
///
|
||||
/// @param [in] src Other Span<T> to copy from
|
||||
template<typename T = void, typename Enabled = IfConst<T, false>>
|
||||
Span(const Span<T>& src) : Span(src.Data(), src.NumElements()) {}
|
||||
|
||||
/// Template constructor from any C++ array
|
||||
///
|
||||
/// @param [in] src C++ array
|
||||
template<typename T, size_t NumElements, typename Enabled = IfConst<T, false>>
|
||||
Span(T(& src)[NumElements]) : Span(&src[0], NumElements) {}
|
||||
|
||||
/// Constructor from any single element
|
||||
///
|
||||
/// @param [in] src Single element
|
||||
template<typename T, typename Enabled = IfConst<T, false>, typename = IfPtr<T, false>>
|
||||
Span(T& src) : Span(&src, 1) {}
|
||||
|
||||
/// Templated conversion of this typeless Span to a typed Subspan
|
||||
///
|
||||
/// @returns A subspan with typed elemeents and NumElements truncated down to the nearest sizeof(type)
|
||||
template<typename T>
|
||||
operator Span<T>() const { return Span<T>(static_cast<T*>(Data()), SizeInBytes() / ElementSize<T>()); }
|
||||
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
template<typename T = Byte>
|
||||
T& At(size_t index) const { return reinterpret_cast<T&>(Base::At(index)); }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage.
|
||||
/// For a non-empty span, the returned pointer contains address of the first element.
|
||||
/// For an empty span, the returned pointer may or may not be a null pointer.
|
||||
template<typename T = void>
|
||||
constexpr T* Data() const { return reinterpret_cast<T*>(Base::Data()); }
|
||||
|
||||
/// Implicitly convert this void Span to its const void equivalent
|
||||
///
|
||||
/// @returns The same span, but of const void type
|
||||
operator Span<const void>() const { return Span<const void>(Data(), NumElements()); }
|
||||
|
||||
/// Returns a "subspan", a view over a subset range of the elements.
|
||||
///
|
||||
/// @warning Behavior is undefined if either
|
||||
/// - offset is greater than NumElements(), or
|
||||
/// - count is not size_t(-1) and is greater than NumElements()-offset.
|
||||
///
|
||||
/// Note that size_t(-1) is equivalent to C++20 std::dynamic_extent, which the C++20 std::span::subspan uses
|
||||
/// in the same way to mean "take the remainder of the elements from offset".
|
||||
///
|
||||
/// @param offset Zero-based offset to start the subspan at
|
||||
/// @param count Number of elements in the subspan, or size_t(-1) for the remainder of the elements from offset
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<T> Subspan(size_t offset, size_t count) const { return Span<void>(Base::Subspan(offset, count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the front.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the front
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<T> DropFront(size_t count = 1) const { return Span<void>(Base::DropFront(count)); }
|
||||
|
||||
/// Returns a subspan dropping the specified number (default 1) of elements from the back.
|
||||
/// Returns an empty Span if there were no more elements than that to start with.
|
||||
///
|
||||
/// @param count Number of elements to drop from the back
|
||||
///
|
||||
/// @returns The subspan
|
||||
template<typename T = void>
|
||||
Span<T> DropBack(size_t count = 1) const { return Span<void>(Base::DropBack(count)); }
|
||||
};
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,188 +1,188 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2020-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palStringUtil.h
|
||||
* @brief PAL String utility collection functions.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <type_traits>
|
||||
|
||||
#include "palUtil.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
#if PAL_CLIENT_INTERFACE_MAJOR_VERSION < 919
|
||||
/// Returns the length of a wchar_t based string.
|
||||
///
|
||||
/// @param [in] wide string to query
|
||||
///
|
||||
/// @returns The length of the given string in wide characters
|
||||
extern size_t PalWcslen(
|
||||
const wchar_t* pWideStr);
|
||||
|
||||
/// Performs a reverse string find of wide character wc.
|
||||
///
|
||||
/// @param [in] wide string to scan
|
||||
/// @param [in] wide character to find
|
||||
///
|
||||
/// @returns The matching character at the end of the string or nullptr if not found.
|
||||
extern wchar_t* PalWcsrchr(
|
||||
wchar_t *pStr,
|
||||
wchar_t wc);
|
||||
#endif
|
||||
|
||||
/// When the -fshort-char compiler option is specified, wchar_t is 16 bits, but mbstowcs still treats the dest
|
||||
/// as 32 bit so we provide our own implementation.
|
||||
///
|
||||
/// @param [out] dst string
|
||||
/// @param [in] src string
|
||||
/// @param [in] size of the destination buffer in words
|
||||
///
|
||||
/// @returns Returns whether or not the conversion was successful.
|
||||
extern bool ConvertCharStringToUtf16(
|
||||
wchar_t* pDst,
|
||||
const char* pSrc,
|
||||
size_t dstSizeInWords);
|
||||
|
||||
/// When the -fshort-char compiler option is specified, wchar_t is 16 bits, but wcstombs still treats the src
|
||||
/// as 32 bit so we provide our own implementation.
|
||||
///
|
||||
/// @param [out] dst string
|
||||
/// @param [in] src string
|
||||
/// @param [in] size of the destination buffer in bytes
|
||||
///
|
||||
/// @returns Returns whether or not the conversion was successful.
|
||||
extern bool ConvertUtf16StringToUtf8(
|
||||
char* pDst,
|
||||
const wchar_t* pSrc,
|
||||
size_t dstSizeInBytes);
|
||||
|
||||
/// Convert wchar_t string to UTF-8 string. Works whether wchar_t is 16 or 32 bits.
|
||||
/// If wchar_t is 16 bits, this decodes UTF-16.
|
||||
///
|
||||
/// @param [out] dst string
|
||||
/// @param [in] src string
|
||||
/// @param [in] size of the destination buffer in bytes
|
||||
///
|
||||
/// @returns Returns whether or not the conversion was successful.
|
||||
bool ConvertWcharStringToUtf8(char* pDst, const wchar_t* pSrc, size_t dstSizeInBytes);
|
||||
|
||||
/// When the -fshort-char compiler option is specified, wchar_t is 16 bits, but wcsncpy still treats its arguments
|
||||
/// as 32 bit so we provide our own implementation.
|
||||
///
|
||||
/// @param [out] pDst Destination string.
|
||||
/// @param [in] pSrc Source string to copy.
|
||||
/// @param [in] dstSize Length of the destination buffer, in wchar_t's.
|
||||
extern void CopyUtf16String(
|
||||
wchar_t* pDst,
|
||||
const wchar_t* pSrc,
|
||||
size_t dstSize);
|
||||
|
||||
/// A shared helper function which takes an arbitrary blob of data and formats it into a human readable "memory view"
|
||||
/// string. This is intended to be used by logging code.
|
||||
///
|
||||
/// Imagine your input buffer is: { 0xef, 0xbe, 0xad, 0xde, 0x78, 0x56, 0x34, 0x12, 0xab }, then the string looks like
|
||||
/// this with a blockSize of 4: "0xdeadbeef 0x12345678 0xab". So the block size determines how many bytes are combined
|
||||
/// into one "0x" character block. The whole block is effetively cast into an integer of that size and printed in big
|
||||
/// endian. Trailing bytes are printed without being size-extended. If a block won't fit at the end of the string it
|
||||
/// is skipped (update your buffer pointer and call again to continue).
|
||||
///
|
||||
/// The return value is the number of bytes consumed from pBuffer. The idea is that you can loop until the full size
|
||||
/// is consumed, printing a new line for each call.
|
||||
///
|
||||
/// @param [out] pDst The caller-provided destination string.
|
||||
/// @param [in] dstSize The length of pDst in bytes.
|
||||
/// @param [in] pBuffer The arbitrary data blob to turn into a string.
|
||||
/// @param [in] bufferSize The length of pBuffer in bytes.
|
||||
/// @param [in] blockSize How many bytes to combine into one hexidecimal big endian string.
|
||||
///
|
||||
/// @returns The number of bytes from pBuffer that were formatted into pDst.
|
||||
extern size_t BytesToStr(
|
||||
char* pDst,
|
||||
size_t dstSize,
|
||||
const void* pBuffer,
|
||||
size_t bufferSize,
|
||||
size_t blockSize);
|
||||
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// @returns String length.
|
||||
constexpr uint32 StringLength(
|
||||
const char* pString)
|
||||
{
|
||||
// TODO: On C++23 we can replace this with consteval-if.
|
||||
// TODO: When we upgrade PAL_CPLUSPLUS then we can rely that std::is_constant_evaluated() is always defined.
|
||||
#if defined(__cpp_lib_is_constant_evaluated)
|
||||
if (std::is_constant_evaluated())
|
||||
#else
|
||||
if (__builtin_is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
uint32 length = 0;
|
||||
while (pString[length] != '\0')
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return uint32(std::strlen(pString));
|
||||
}
|
||||
}
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// @returns String length.
|
||||
constexpr uint32 StringLength(
|
||||
const wchar_t* pString)
|
||||
{
|
||||
// TODO: On C++23 we can replace this with consteval-if.
|
||||
// TODO: When we upgrade PAL_CPLUSPLUS then we can rely that std::is_constant_evaluated() is always defined.
|
||||
#if defined(__cpp_lib_is_constant_evaluated)
|
||||
if (std::is_constant_evaluated())
|
||||
#else
|
||||
if (__builtin_is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
uint32 length = 0;
|
||||
while (pString[length] != L'\0')
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return uint32(std::wcslen(pString));
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2020-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palStringUtil.h
|
||||
* @brief PAL String utility collection functions.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <type_traits>
|
||||
|
||||
#include "palUtil.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
#if PAL_CLIENT_INTERFACE_MAJOR_VERSION < 919
|
||||
/// Returns the length of a wchar_t based string.
|
||||
///
|
||||
/// @param [in] wide string to query
|
||||
///
|
||||
/// @returns The length of the given string in wide characters
|
||||
extern size_t PalWcslen(
|
||||
const wchar_t* pWideStr);
|
||||
|
||||
/// Performs a reverse string find of wide character wc.
|
||||
///
|
||||
/// @param [in] wide string to scan
|
||||
/// @param [in] wide character to find
|
||||
///
|
||||
/// @returns The matching character at the end of the string or nullptr if not found.
|
||||
extern wchar_t* PalWcsrchr(
|
||||
wchar_t *pStr,
|
||||
wchar_t wc);
|
||||
#endif
|
||||
|
||||
/// When the -fshort-char compiler option is specified, wchar_t is 16 bits, but mbstowcs still treats the dest
|
||||
/// as 32 bit so we provide our own implementation.
|
||||
///
|
||||
/// @param [out] dst string
|
||||
/// @param [in] src string
|
||||
/// @param [in] size of the destination buffer in words
|
||||
///
|
||||
/// @returns Returns whether or not the conversion was successful.
|
||||
extern bool ConvertCharStringToUtf16(
|
||||
wchar_t* pDst,
|
||||
const char* pSrc,
|
||||
size_t dstSizeInWords);
|
||||
|
||||
/// When the -fshort-char compiler option is specified, wchar_t is 16 bits, but wcstombs still treats the src
|
||||
/// as 32 bit so we provide our own implementation.
|
||||
///
|
||||
/// @param [out] dst string
|
||||
/// @param [in] src string
|
||||
/// @param [in] size of the destination buffer in bytes
|
||||
///
|
||||
/// @returns Returns whether or not the conversion was successful.
|
||||
extern bool ConvertUtf16StringToUtf8(
|
||||
char* pDst,
|
||||
const wchar_t* pSrc,
|
||||
size_t dstSizeInBytes);
|
||||
|
||||
/// Convert wchar_t string to UTF-8 string. Works whether wchar_t is 16 or 32 bits.
|
||||
/// If wchar_t is 16 bits, this decodes UTF-16.
|
||||
///
|
||||
/// @param [out] dst string
|
||||
/// @param [in] src string
|
||||
/// @param [in] size of the destination buffer in bytes
|
||||
///
|
||||
/// @returns Returns whether or not the conversion was successful.
|
||||
bool ConvertWcharStringToUtf8(char* pDst, const wchar_t* pSrc, size_t dstSizeInBytes);
|
||||
|
||||
/// When the -fshort-char compiler option is specified, wchar_t is 16 bits, but wcsncpy still treats its arguments
|
||||
/// as 32 bit so we provide our own implementation.
|
||||
///
|
||||
/// @param [out] pDst Destination string.
|
||||
/// @param [in] pSrc Source string to copy.
|
||||
/// @param [in] dstSize Length of the destination buffer, in wchar_t's.
|
||||
extern void CopyUtf16String(
|
||||
wchar_t* pDst,
|
||||
const wchar_t* pSrc,
|
||||
size_t dstSize);
|
||||
|
||||
/// A shared helper function which takes an arbitrary blob of data and formats it into a human readable "memory view"
|
||||
/// string. This is intended to be used by logging code.
|
||||
///
|
||||
/// Imagine your input buffer is: { 0xef, 0xbe, 0xad, 0xde, 0x78, 0x56, 0x34, 0x12, 0xab }, then the string looks like
|
||||
/// this with a blockSize of 4: "0xdeadbeef 0x12345678 0xab". So the block size determines how many bytes are combined
|
||||
/// into one "0x" character block. The whole block is effetively cast into an integer of that size and printed in big
|
||||
/// endian. Trailing bytes are printed without being size-extended. If a block won't fit at the end of the string it
|
||||
/// is skipped (update your buffer pointer and call again to continue).
|
||||
///
|
||||
/// The return value is the number of bytes consumed from pBuffer. The idea is that you can loop until the full size
|
||||
/// is consumed, printing a new line for each call.
|
||||
///
|
||||
/// @param [out] pDst The caller-provided destination string.
|
||||
/// @param [in] dstSize The length of pDst in bytes.
|
||||
/// @param [in] pBuffer The arbitrary data blob to turn into a string.
|
||||
/// @param [in] bufferSize The length of pBuffer in bytes.
|
||||
/// @param [in] blockSize How many bytes to combine into one hexidecimal big endian string.
|
||||
///
|
||||
/// @returns The number of bytes from pBuffer that were formatted into pDst.
|
||||
extern size_t BytesToStr(
|
||||
char* pDst,
|
||||
size_t dstSize,
|
||||
const void* pBuffer,
|
||||
size_t bufferSize,
|
||||
size_t blockSize);
|
||||
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// @returns String length.
|
||||
constexpr uint32 StringLength(
|
||||
const char* pString)
|
||||
{
|
||||
// TODO: On C++23 we can replace this with consteval-if.
|
||||
// TODO: When we upgrade PAL_CPLUSPLUS then we can rely that std::is_constant_evaluated() is always defined.
|
||||
#if defined(__cpp_lib_is_constant_evaluated)
|
||||
if (std::is_constant_evaluated())
|
||||
#else
|
||||
if (__builtin_is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
uint32 length = 0;
|
||||
while (pString[length] != '\0')
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return uint32(std::strlen(pString));
|
||||
}
|
||||
}
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// @returns String length.
|
||||
constexpr uint32 StringLength(
|
||||
const wchar_t* pString)
|
||||
{
|
||||
// TODO: On C++23 we can replace this with consteval-if.
|
||||
// TODO: When we upgrade PAL_CPLUSPLUS then we can rely that std::is_constant_evaluated() is always defined.
|
||||
#if defined(__cpp_lib_is_constant_evaluated)
|
||||
if (std::is_constant_evaluated())
|
||||
#else
|
||||
if (__builtin_is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
uint32 length = 0;
|
||||
while (pString[length] != L'\0')
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return uint32(std::wcslen(pString));
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
@@ -1,228 +1,228 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2021-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palStringView.h
|
||||
* @brief PAL utility collection string view declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palAssert.h"
|
||||
#include "palInlineFuncs.h"
|
||||
#include "palStringUtil.h"
|
||||
#include "palUtil.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief String view.
|
||||
*
|
||||
* A StringView is a templated view over a constant contiguous sequence of characters.
|
||||
*
|
||||
* @warning The string view assumes that its lifetime does not extend past that of the pointed-to character sequence.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename CharT>
|
||||
class StringView
|
||||
{
|
||||
static_assert((std::is_same<CharT, char>::value || std::is_same<CharT, wchar_t>::value),
|
||||
"StringView type T must be either char or wchar_t.");
|
||||
public:
|
||||
/// Constructs an empty StringView.
|
||||
constexpr StringView()
|
||||
:
|
||||
m_pData{},
|
||||
m_length{}
|
||||
{}
|
||||
|
||||
constexpr StringView(
|
||||
const CharT* s,
|
||||
uint32 count)
|
||||
:
|
||||
m_pData{s},
|
||||
m_length{count}
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((s != nullptr) || (count == 0));
|
||||
}
|
||||
|
||||
StringView(
|
||||
const CharT* s)
|
||||
:
|
||||
StringView()
|
||||
{
|
||||
if (s != nullptr)
|
||||
{
|
||||
m_length = StringLength(s);
|
||||
m_pData = s;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr StringView(std::nullptr_t) = delete;
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
constexpr const CharT& At(uint32 index) const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(index < Length());
|
||||
return m_pData[index];
|
||||
}
|
||||
|
||||
constexpr const CharT& operator[](uint32 index) const { return At(index); }
|
||||
///@}
|
||||
|
||||
/// Returns pointer to the underlying string serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + Length()),
|
||||
/// even if the view does not point to any data storage (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @warning Dereferencing pointer returned by Data() from a view that does not point to a data storage will cause
|
||||
/// an access violation!
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read access.
|
||||
/// For a view to a valid data storage, the returned pointer contains address of the first element.
|
||||
/// For a view without a valid data storage, the returned pointer will be a @c nullptr.
|
||||
constexpr const CharT* Data() const noexcept { return m_pData; }
|
||||
|
||||
/// Returns the data at the front of the view.
|
||||
///
|
||||
/// @warning Calling this function on an empty view will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the front of the view.
|
||||
constexpr const CharT& Front() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return m_pData[0];
|
||||
}
|
||||
|
||||
/// Returns the data at the back of the view.
|
||||
///
|
||||
/// @warning Calling this function on an empty view will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the back of the view.
|
||||
constexpr const CharT& Back() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return m_pData[Length() - 1];
|
||||
}
|
||||
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the length of the string.
|
||||
constexpr uint32 Length() const { return m_length; }
|
||||
|
||||
/// Returns true if the number of characters the view points to is equal to zero.
|
||||
///
|
||||
/// @returns True if the view points to an empty or non-existing data storage.
|
||||
constexpr bool IsEmpty() const { return (m_length == 0); }
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using CharT* as `iterator`.
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by C++ language features such as `range-based for`.
|
||||
/// These should not be called directly as they do not adhere to PAL coding standards.
|
||||
using const_iterator = const CharT*;
|
||||
|
||||
constexpr const_iterator begin() const noexcept { return m_pData; }
|
||||
constexpr const_iterator end() const noexcept { return m_pData + Length(); }
|
||||
///@}
|
||||
|
||||
private:
|
||||
const CharT* m_pData;
|
||||
uint32 m_length;
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
constexpr bool operator==(
|
||||
StringView<CharT> x,
|
||||
StringView<CharT> y)
|
||||
{
|
||||
bool equal = (x.Length() == y.Length());
|
||||
if (equal)
|
||||
{
|
||||
if (x.Data() != y.Data())
|
||||
{
|
||||
// they are not pointing to the same storage, so we need to compare the contents
|
||||
for (uint32 index = 0; equal && (index < x.Length()); ++index)
|
||||
{
|
||||
equal = (x[index] == y[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
constexpr bool operator!=(StringView<CharT> x, StringView<CharT> y) { return (x == y) == false; }
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator<(
|
||||
StringView<CharT> x,
|
||||
StringView<CharT> y)
|
||||
{
|
||||
const uint32 minLength = Min(x.Length(), y.Length());
|
||||
int compare = strncmp(x.Data(), y.Data(), minLength);
|
||||
if (compare == 0)
|
||||
{
|
||||
// strings are equal up to minLength, so check which is shorter
|
||||
compare = int(x.Length()) - int(y.Length());
|
||||
}
|
||||
|
||||
return compare < 0;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator<=(StringView<CharT> x, StringView<CharT> y) { return (y < x) == false; }
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator>(StringView<CharT> x, StringView<CharT> y) { return y < x; }
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator>=(StringView<CharT> x, StringView<CharT> y) { return (x < y) == false; }
|
||||
|
||||
/// Specialization of @ref HashString(const char*,size_t) for @ref StringView.
|
||||
template<typename T>
|
||||
constexpr uint32 HashString(
|
||||
StringView<T> sv)
|
||||
{
|
||||
return HashString(sv.Data(), sv.Length());
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2021-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palStringView.h
|
||||
* @brief PAL utility collection string view declaration.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palAssert.h"
|
||||
#include "palInlineFuncs.h"
|
||||
#include "palStringUtil.h"
|
||||
#include "palUtil.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief String view.
|
||||
*
|
||||
* A StringView is a templated view over a constant contiguous sequence of characters.
|
||||
*
|
||||
* @warning The string view assumes that its lifetime does not extend past that of the pointed-to character sequence.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename CharT>
|
||||
class StringView
|
||||
{
|
||||
static_assert((std::is_same<CharT, char>::value || std::is_same<CharT, wchar_t>::value),
|
||||
"StringView type T must be either char or wchar_t.");
|
||||
public:
|
||||
/// Constructs an empty StringView.
|
||||
constexpr StringView()
|
||||
:
|
||||
m_pData{},
|
||||
m_length{}
|
||||
{}
|
||||
|
||||
constexpr StringView(
|
||||
const CharT* s,
|
||||
uint32 count)
|
||||
:
|
||||
m_pData{s},
|
||||
m_length{count}
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT((s != nullptr) || (count == 0));
|
||||
}
|
||||
|
||||
StringView(
|
||||
const CharT* s)
|
||||
:
|
||||
StringView()
|
||||
{
|
||||
if (s != nullptr)
|
||||
{
|
||||
m_length = StringLength(s);
|
||||
m_pData = s;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr StringView(std::nullptr_t) = delete;
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
constexpr const CharT& At(uint32 index) const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(index < Length());
|
||||
return m_pData[index];
|
||||
}
|
||||
|
||||
constexpr const CharT& operator[](uint32 index) const { return At(index); }
|
||||
///@}
|
||||
|
||||
/// Returns pointer to the underlying string serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + Length()),
|
||||
/// even if the view does not point to any data storage (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @warning Dereferencing pointer returned by Data() from a view that does not point to a data storage will cause
|
||||
/// an access violation!
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read access.
|
||||
/// For a view to a valid data storage, the returned pointer contains address of the first element.
|
||||
/// For a view without a valid data storage, the returned pointer will be a @c nullptr.
|
||||
constexpr const CharT* Data() const noexcept { return m_pData; }
|
||||
|
||||
/// Returns the data at the front of the view.
|
||||
///
|
||||
/// @warning Calling this function on an empty view will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the front of the view.
|
||||
constexpr const CharT& Front() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return m_pData[0];
|
||||
}
|
||||
|
||||
/// Returns the data at the back of the view.
|
||||
///
|
||||
/// @warning Calling this function on an empty view will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the back of the view.
|
||||
constexpr const CharT& Back() const
|
||||
{
|
||||
PAL_CONSTEXPR_ASSERT(IsEmpty() == false);
|
||||
return m_pData[Length() - 1];
|
||||
}
|
||||
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the length of the string.
|
||||
constexpr uint32 Length() const { return m_length; }
|
||||
|
||||
/// Returns true if the number of characters the view points to is equal to zero.
|
||||
///
|
||||
/// @returns True if the view points to an empty or non-existing data storage.
|
||||
constexpr bool IsEmpty() const { return (m_length == 0); }
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using CharT* as `iterator`.
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by C++ language features such as `range-based for`.
|
||||
/// These should not be called directly as they do not adhere to PAL coding standards.
|
||||
using const_iterator = const CharT*;
|
||||
|
||||
constexpr const_iterator begin() const noexcept { return m_pData; }
|
||||
constexpr const_iterator end() const noexcept { return m_pData + Length(); }
|
||||
///@}
|
||||
|
||||
private:
|
||||
const CharT* m_pData;
|
||||
uint32 m_length;
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
constexpr bool operator==(
|
||||
StringView<CharT> x,
|
||||
StringView<CharT> y)
|
||||
{
|
||||
bool equal = (x.Length() == y.Length());
|
||||
if (equal)
|
||||
{
|
||||
if (x.Data() != y.Data())
|
||||
{
|
||||
// they are not pointing to the same storage, so we need to compare the contents
|
||||
for (uint32 index = 0; equal && (index < x.Length()); ++index)
|
||||
{
|
||||
equal = (x[index] == y[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
constexpr bool operator!=(StringView<CharT> x, StringView<CharT> y) { return (x == y) == false; }
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator<(
|
||||
StringView<CharT> x,
|
||||
StringView<CharT> y)
|
||||
{
|
||||
const uint32 minLength = Min(x.Length(), y.Length());
|
||||
int compare = strncmp(x.Data(), y.Data(), minLength);
|
||||
if (compare == 0)
|
||||
{
|
||||
// strings are equal up to minLength, so check which is shorter
|
||||
compare = int(x.Length()) - int(y.Length());
|
||||
}
|
||||
|
||||
return compare < 0;
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator<=(StringView<CharT> x, StringView<CharT> y) { return (y < x) == false; }
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator>(StringView<CharT> x, StringView<CharT> y) { return y < x; }
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename CharT>
|
||||
bool operator>=(StringView<CharT> x, StringView<CharT> y) { return (x < y) == false; }
|
||||
|
||||
/// Specialization of @ref HashString(const char*,size_t) for @ref StringView.
|
||||
template<typename T>
|
||||
constexpr uint32 HashString(
|
||||
StringView<T> sv)
|
||||
{
|
||||
return HashString(sv.Data(), sv.Length());
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,87 +1,87 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2021-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palTime.h
|
||||
* @brief PAL time-related utility collection.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Specifies a class that implements a timestamp.
|
||||
class Timestamp
|
||||
{
|
||||
public:
|
||||
/// Creates a new timestamp object that records the time it was created.
|
||||
Timestamp();
|
||||
|
||||
/// Returns the timestamp as a C-string.
|
||||
const char* CStr() const { return m_data; }
|
||||
|
||||
private:
|
||||
char m_data[64];
|
||||
};
|
||||
|
||||
#if PAL_CLIENT_INTERFACE_MAJOR_VERSION >= 873
|
||||
/// Seconds stored as a float instead of an integer.
|
||||
using fseconds = std::chrono::duration<float>;
|
||||
/// Milliseconds stored as a float instead of an integer.
|
||||
using fmilliseconds = std::chrono::duration<float, std::milli>;
|
||||
/// Microseconds stored as a float instead of an integer.
|
||||
using fmicroseconds = std::chrono::duration<float, std::micro>;
|
||||
/// Nanoseconds stored as a float instead of an integer.
|
||||
using fnanoseconds = std::chrono::duration<float, std::nano>;
|
||||
|
||||
/// A time_point who's epoch is January 1st 1970 and uses seconds for the duration.
|
||||
/// C++20 guarantees us that system_clock's epoch is always January 1st 1970 on all platforms.
|
||||
/// system_clock's internal duration is still implementation defined.
|
||||
/// On Windows it's hundreds-of-nanoseconds and on Linux it's seconds.
|
||||
/// However time_point has it's own duration type.
|
||||
/// As long as we go through the time_point to interpret the duration then everything should be in terms of seconds.
|
||||
using SecondsSinceEpoch = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
|
||||
/// Like std::chrono::duration_cast, but it preserves the special 'infinite' value used in timeouts.
|
||||
template<class DestDuration, class Rep, class Period>
|
||||
constexpr DestDuration TimeoutCast(
|
||||
const std::chrono::duration<Rep, Period>& d)
|
||||
{
|
||||
if (d == (std::chrono::duration<Rep, Period>::max)())
|
||||
{
|
||||
return (DestDuration::max)();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::chrono::duration_cast<DestDuration, Rep, Period>(d);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2021-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palTime.h
|
||||
* @brief PAL time-related utility collection.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
/// Specifies a class that implements a timestamp.
|
||||
class Timestamp
|
||||
{
|
||||
public:
|
||||
/// Creates a new timestamp object that records the time it was created.
|
||||
Timestamp();
|
||||
|
||||
/// Returns the timestamp as a C-string.
|
||||
const char* CStr() const { return m_data; }
|
||||
|
||||
private:
|
||||
char m_data[64];
|
||||
};
|
||||
|
||||
#if PAL_CLIENT_INTERFACE_MAJOR_VERSION >= 873
|
||||
/// Seconds stored as a float instead of an integer.
|
||||
using fseconds = std::chrono::duration<float>;
|
||||
/// Milliseconds stored as a float instead of an integer.
|
||||
using fmilliseconds = std::chrono::duration<float, std::milli>;
|
||||
/// Microseconds stored as a float instead of an integer.
|
||||
using fmicroseconds = std::chrono::duration<float, std::micro>;
|
||||
/// Nanoseconds stored as a float instead of an integer.
|
||||
using fnanoseconds = std::chrono::duration<float, std::nano>;
|
||||
|
||||
/// A time_point who's epoch is January 1st 1970 and uses seconds for the duration.
|
||||
/// C++20 guarantees us that system_clock's epoch is always January 1st 1970 on all platforms.
|
||||
/// system_clock's internal duration is still implementation defined.
|
||||
/// On Windows it's hundreds-of-nanoseconds and on Linux it's seconds.
|
||||
/// However time_point has it's own duration type.
|
||||
/// As long as we go through the time_point to interpret the duration then everything should be in terms of seconds.
|
||||
using SecondsSinceEpoch = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
|
||||
/// Like std::chrono::duration_cast, but it preserves the special 'infinite' value used in timeouts.
|
||||
template<class DestDuration, class Rep, class Period>
|
||||
constexpr DestDuration TimeoutCast(
|
||||
const std::chrono::duration<Rep, Period>& d)
|
||||
{
|
||||
if (d == (std::chrono::duration<Rep, Period>::max)())
|
||||
{
|
||||
return (DestDuration::max)();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::chrono::duration_cast<DestDuration, Rep, Period>(d);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // Util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,455 +1,455 @@
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palVector.h
|
||||
* @brief PAL utility collection Vector and VectorIterator class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palAssert.h"
|
||||
#include "palSpan.h"
|
||||
#include "palSysMemory.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator> class Vector;
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Iterator for traversal of elements in Vector.
|
||||
*
|
||||
* Supports forward traversal.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
class VectorIterator
|
||||
{
|
||||
public:
|
||||
/// Checks if the current index is within bounds of the number of elements in the vector.
|
||||
///
|
||||
/// @returns True if the current element this iterator is pointing to is within the permitted range.
|
||||
bool IsValid() const { return (m_curIndex < m_srcVector.m_numElements); }
|
||||
|
||||
/// Returns the element the iterator is currently pointing to as a reference.
|
||||
///
|
||||
/// @warning This may cause an access violation if the iterator is not valid.
|
||||
///
|
||||
/// @returns The element the iterator is currently pointing to.
|
||||
T& Get() const
|
||||
{
|
||||
PAL_ASSERT(IsValid());
|
||||
return (*(m_srcVector.m_pData + m_curIndex));
|
||||
}
|
||||
|
||||
/// Advances the iterator to point to the next element.
|
||||
///
|
||||
/// @warning Does not do bounds checking.
|
||||
void Next() { ++m_curIndex; }
|
||||
|
||||
/// Retrieves the current vector position of this iterator.
|
||||
///
|
||||
/// @returns The location in the vector of the element the iterator is currently pointing to.
|
||||
uint32 Position() const { return m_curIndex; }
|
||||
|
||||
private:
|
||||
VectorIterator(uint32 index, const Vector<T, defaultCapacity, Allocator>& srcVec);
|
||||
|
||||
uint32 m_curIndex; // The current index of the vector iterator.
|
||||
const Vector<T, defaultCapacity, Allocator>& m_srcVector; // The vector container this iterator is used for.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(VectorIterator);
|
||||
|
||||
// Although this is a transgression of coding standards, it means that Vector does not need to have a public
|
||||
// interface specifically to implement this class. The added encapsulation this provides is worthwhile.
|
||||
friend class Vector<T, defaultCapacity, Allocator>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Vector container.
|
||||
*
|
||||
* Vector is a templated array based storage that starts with a default-size allocation in the stack. If more space is
|
||||
* needed it then resorts to dynamic allocation by doubling the size every time the capacity is exceeded.
|
||||
* Operations which this class supports are:
|
||||
*
|
||||
* - Insertion at the end of the array.
|
||||
* - Forward iteration.
|
||||
* - Random access.
|
||||
*
|
||||
* @warning This class is not thread-safe.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
class Vector
|
||||
{
|
||||
public:
|
||||
/// A convenient shorthand for VectorIterator.
|
||||
typedef VectorIterator<T, defaultCapacity, Allocator> Iter;
|
||||
|
||||
/// When this allocates, it doubles the old size of memory
|
||||
static constexpr uint32 GrowthFactor = 2;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
Vector(Allocator*const pAllocator);
|
||||
|
||||
/// Destructor.
|
||||
~Vector();
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// @param [in] vector Reference to a dying vector, from which resources will be stolen.
|
||||
Vector(Vector&& vector);
|
||||
|
||||
/// Increases maximal capacity to value greater or equal to the newCapacity.
|
||||
/// If newCapacity is greater than the maximal capacity, new storage is allocated,
|
||||
/// otherwise the method does nothing.
|
||||
///
|
||||
/// @note All existing iterators will not get invalidated, even in case new storage is allocated,
|
||||
/// because iterators are referencing vector, rather than elements of that vector.
|
||||
///
|
||||
/// @warning All pointers and references to elements of a vector will be invalidated,
|
||||
/// in case new storage is allocated.
|
||||
///
|
||||
/// @param [in] newCapacity The new capacity of a vector, which is lower limit of the maximal capacity.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result Reserve(uint32 newCapacity);
|
||||
|
||||
/// Increases maximum capacity to the number of elements in the vector, plus the specified increment amount.
|
||||
/// Equivalent to this->Reserve(this->NumElements() + amount);
|
||||
///
|
||||
/// @param [in] amount Number of items beyond the current element count to increas the capacity to.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result Grow(uint32 amount) { return Reserve(NumElements() + amount); }
|
||||
|
||||
/// Set size to newSize.
|
||||
/// If size is decreased, elements at the end of the vector will be removed.
|
||||
/// If size is increased, new elements will be set to newVal.
|
||||
/// If newSize requires a higher capacity, a new allocation is made. See notes on Reserve.
|
||||
///
|
||||
/// @note If size is decreased, any iterators referencing removed elements will become invalid. All other
|
||||
/// iterators will remain valid. Otherwise, all iterators will still be valid.
|
||||
///
|
||||
/// @warning All pointers and references to elements of a vector will be invalidated,
|
||||
/// in case new storage is allocated.
|
||||
///
|
||||
/// @param [in] newSize The new size of a vector.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result Resize(uint32 newSize, const T& newVal = T());
|
||||
|
||||
/// Copy/Move an element to end of the vector. If not enough space is available, new space will be allocated and
|
||||
/// the old data will be copied to the new space.
|
||||
///
|
||||
/// @param [in] data The element to be pushed to the vector. The element will become the last element.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result PushBack(const T& data);
|
||||
Result PushBack(T&& data);
|
||||
|
||||
/// Constructs an object in-place at the end of the vector. If not enough space is available, new space will be
|
||||
/// allocated and the old data will be copied to the new space.
|
||||
///
|
||||
/// @param [in] args... The arguments passed to the constructor
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
template <typename... Args>
|
||||
Result EmplaceBack(Args&&... args);
|
||||
|
||||
/// Returns the element at the end of the vector and destroys it.
|
||||
///
|
||||
/// @param [out] pData The element at the end of the vector.
|
||||
/// It is expected that pData is uninitialized as it will be overwritten and not destructed.
|
||||
void PopBack(T* pData);
|
||||
|
||||
/// Destroys all elements stored in the vector. All dynamically allocated memory will be saved for reuse.
|
||||
void Clear();
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
T& At(uint32 index)
|
||||
{
|
||||
PAL_ASSERT(index < m_numElements);
|
||||
return *(m_pData + index);
|
||||
}
|
||||
|
||||
const T& At(uint32 index) const
|
||||
{
|
||||
PAL_ASSERT(index < m_numElements);
|
||||
return *(m_pData + index);
|
||||
}
|
||||
|
||||
T& operator[](uint32 index) noexcept { return At(index); }
|
||||
const T& operator[](uint32 index) const noexcept { return At(index); }
|
||||
///@}
|
||||
|
||||
/// Returns the data at the front of the vector.
|
||||
///
|
||||
/// @warning Calling this function on an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the front of the vector.
|
||||
T& Front() const
|
||||
{
|
||||
PAL_ASSERT(IsEmpty() == false);
|
||||
return *m_pData;
|
||||
}
|
||||
|
||||
/// Returns the data at the back of the vector.
|
||||
///
|
||||
/// @warning Calling this function on an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the back of the vector.
|
||||
T& Back() const
|
||||
{
|
||||
PAL_ASSERT(IsEmpty() == false);
|
||||
return *(m_pData + (m_numElements - 1));
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first element of the vector.
|
||||
///
|
||||
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns An iterator to first element of the vector.
|
||||
Iter Begin() const { return Iter(0, *this); }
|
||||
|
||||
/// Returns an iterator to the last element of the vector.
|
||||
///
|
||||
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns VectorIterator An iterator to last element of the vector.
|
||||
Iter End() const { return Iter((m_numElements - 1), *this); }
|
||||
|
||||
///@{
|
||||
/// Implicitly gets the current contents of the vector as a Span.
|
||||
///
|
||||
/// @returns The contents of the vector as a Span; same as Span<T>(Data(), NumElements()).
|
||||
operator Span<T>() { return Span<T>(Data(), NumElements()); }
|
||||
operator Span<const T>() const { return Span<const T>(Data(), NumElements()); }
|
||||
///@}
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + NumElements()),
|
||||
/// even if the container is empty (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @warning Dereferencing pointer returned by Data() from an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read & write access.
|
||||
/// For a non-empty vector, the returned pointer contains address of the first element.
|
||||
/// For an empty vector, the returned pointer may or may not be a null pointer.
|
||||
T* Data() { return m_pData; }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + NumElements()),
|
||||
/// even if the container is empty (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @warning Dereferencing pointer returned by Data() from an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read only access.
|
||||
/// For a non-empty vector, the returned pointer contains address of the first element.
|
||||
/// For an empty vector, the returned pointer may or may not be a null pointer.
|
||||
const T* Data() const { return m_pData; }
|
||||
|
||||
/// Returns the size of the vector.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the number of elements currently present in the vector.
|
||||
uint32 NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns true if the number of elements present in the vector is equal to zero.
|
||||
///
|
||||
/// @returns True if the vector is empty.
|
||||
bool IsEmpty() const { return (m_numElements == 0); }
|
||||
|
||||
/// Returns a pointer to the allocator used for this container's memory management.
|
||||
///
|
||||
/// @returns Allocator pointer.
|
||||
Allocator* GetAllocator() const { return m_pAllocator; }
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using T* as `iterator` and 32-bit size and difference types
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by c++ language features such as `range for`.
|
||||
/// These should not be called directly as they do not adhere to PAL coding standards.
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
using difference_type = int32;
|
||||
using size_type = uint32;
|
||||
|
||||
iterator begin() noexcept { return m_pData; }
|
||||
iterator end() noexcept { return (m_pData + m_numElements); }
|
||||
const_iterator begin() const noexcept { return m_pData; }
|
||||
const_iterator end() const noexcept { return (m_pData + m_numElements); }
|
||||
const_iterator cbegin() const noexcept { return m_pData; }
|
||||
const_iterator cend() const noexcept { return (m_pData + m_numElements); }
|
||||
[[nodiscard]] bool empty() const noexcept { return IsEmpty(); }
|
||||
size_type size() const noexcept { return m_numElements; }
|
||||
///@}
|
||||
|
||||
/// Erases the element at the specified iterator.
|
||||
void Erase(Iter it);
|
||||
|
||||
/// Erases the element at the specified iterator.
|
||||
void Erase(iterator it);
|
||||
|
||||
/// Erases the element at the specified index.
|
||||
void Erase(uint32 index);
|
||||
|
||||
/// Erase the element at the specified iterator, and swap last element to that position.
|
||||
/// If the element to erase is the last element, erase directly and no swap operation.
|
||||
void EraseAndSwapLast(Iter it);
|
||||
|
||||
/// Erase the element at the specified iterator, and swap last element to that position.
|
||||
/// If the element to erase is the last element, erase directly and no swap operation.
|
||||
void EraseAndSwapLast(iterator it);
|
||||
|
||||
/// Erases the element at the specified index, and swap last element to that position.
|
||||
/// If the element to erase is the last element, erase directly and no swap operation.
|
||||
void EraseAndSwapLast(uint32 index);
|
||||
|
||||
private:
|
||||
// This is a POD-type that exactly fits one T value.
|
||||
typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type ValueStorage;
|
||||
|
||||
ValueStorage m_data[defaultCapacity]; // The initial data buffer stored within the vector object.
|
||||
T* m_pData; // Pointer to the current data buffer.
|
||||
uint32 m_numElements; // Number of elements present.
|
||||
uint32 m_maxCapacity; // Maximum size it can hold.
|
||||
Allocator*const m_pAllocator; // Allocator for this Vector.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Vector);
|
||||
|
||||
// Although this is a transgression of coding standards, it prevents VectorIterator requiring a public constructor;
|
||||
// constructing a 'bare' VectorIterator (i.e. without calling Vector::GetIterator) can never be a legal operation,
|
||||
// so this means that these two classes are much safer to use.
|
||||
friend class VectorIterator<T, defaultCapacity, Allocator>;
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
VectorIterator<T, defaultCapacity, Allocator>::VectorIterator(
|
||||
uint32 index,
|
||||
const Vector<T, defaultCapacity, Allocator>& srcVec)
|
||||
:
|
||||
m_curIndex(index),
|
||||
m_srcVector(srcVec)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
Vector<T, defaultCapacity, Allocator>::Vector(
|
||||
Allocator*const pAllocator)
|
||||
:
|
||||
m_pData(reinterpret_cast<T*>(m_data)),
|
||||
m_numElements(0),
|
||||
m_maxCapacity(defaultCapacity),
|
||||
m_pAllocator(pAllocator)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
Vector<T, defaultCapacity, Allocator>::~Vector()
|
||||
{
|
||||
// Explicitly destroy all non-trivial types.
|
||||
if (!std::is_trivial<T>::value)
|
||||
{
|
||||
for (uint32 idx = 0; idx < m_numElements; ++idx)
|
||||
{
|
||||
m_pData[idx].~T();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have dynamically allocated memory.
|
||||
if (m_pData != reinterpret_cast<T*>(m_data))
|
||||
{
|
||||
// Free the memory that was allocated dynamically.
|
||||
PAL_FREE(m_pData, m_pAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Steals allocation from a dying vector, if data buffer uses storage from heap allocation.
|
||||
// Moves objects between local buffers of new and dying vectors (for non-trivial types) or
|
||||
// copies local buffer from a dying vector to a new vector (for trivial types),
|
||||
// if data buffer uses storage from local buffer.
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
Vector<T, defaultCapacity, Allocator>::Vector(
|
||||
Vector&& vector)
|
||||
:
|
||||
m_numElements(vector.m_numElements),
|
||||
m_maxCapacity(vector.m_maxCapacity),
|
||||
m_pAllocator(vector.m_pAllocator)
|
||||
{
|
||||
if (vector.m_pData == reinterpret_cast<T*>(vector.m_data)) // Local buffer
|
||||
{
|
||||
// Data buffer will be using storage from local buffer.
|
||||
m_pData = reinterpret_cast<T*>(m_data);
|
||||
|
||||
if (std::is_trivial<T>::value)
|
||||
{
|
||||
// Optimize trivial types by copying local buffer.
|
||||
std::memcpy(m_pData, vector.m_pData, sizeof(T) * m_numElements);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move objects from local buffer of a dying vector to local buffer of a new vector.
|
||||
for (uint32 idx = 0; idx < m_numElements; ++idx)
|
||||
{
|
||||
PAL_PLACEMENT_NEW(m_pData + idx) T(Move(vector.m_pData[idx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Heap allocation
|
||||
{
|
||||
// Steal heap allocation from dying vector.
|
||||
m_pData = vector.m_pData;
|
||||
|
||||
// After the allocation has been stolen, dying vector is just an empty shell.
|
||||
vector.m_pData = nullptr;
|
||||
vector.m_numElements = 0;
|
||||
vector.m_maxCapacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @file palVector.h
|
||||
* @brief PAL utility collection Vector and VectorIterator class declarations.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "palUtil.h"
|
||||
#include "palAssert.h"
|
||||
#include "palSpan.h"
|
||||
#include "palSysMemory.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Util
|
||||
{
|
||||
|
||||
// Forward declarations.
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator> class Vector;
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Iterator for traversal of elements in Vector.
|
||||
*
|
||||
* Supports forward traversal.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
class VectorIterator
|
||||
{
|
||||
public:
|
||||
/// Checks if the current index is within bounds of the number of elements in the vector.
|
||||
///
|
||||
/// @returns True if the current element this iterator is pointing to is within the permitted range.
|
||||
bool IsValid() const { return (m_curIndex < m_srcVector.m_numElements); }
|
||||
|
||||
/// Returns the element the iterator is currently pointing to as a reference.
|
||||
///
|
||||
/// @warning This may cause an access violation if the iterator is not valid.
|
||||
///
|
||||
/// @returns The element the iterator is currently pointing to.
|
||||
T& Get() const
|
||||
{
|
||||
PAL_ASSERT(IsValid());
|
||||
return (*(m_srcVector.m_pData + m_curIndex));
|
||||
}
|
||||
|
||||
/// Advances the iterator to point to the next element.
|
||||
///
|
||||
/// @warning Does not do bounds checking.
|
||||
void Next() { ++m_curIndex; }
|
||||
|
||||
/// Retrieves the current vector position of this iterator.
|
||||
///
|
||||
/// @returns The location in the vector of the element the iterator is currently pointing to.
|
||||
uint32 Position() const { return m_curIndex; }
|
||||
|
||||
private:
|
||||
VectorIterator(uint32 index, const Vector<T, defaultCapacity, Allocator>& srcVec);
|
||||
|
||||
uint32 m_curIndex; // The current index of the vector iterator.
|
||||
const Vector<T, defaultCapacity, Allocator>& m_srcVector; // The vector container this iterator is used for.
|
||||
|
||||
PAL_DISALLOW_DEFAULT_CTOR(VectorIterator);
|
||||
|
||||
// Although this is a transgression of coding standards, it means that Vector does not need to have a public
|
||||
// interface specifically to implement this class. The added encapsulation this provides is worthwhile.
|
||||
friend class Vector<T, defaultCapacity, Allocator>;
|
||||
};
|
||||
|
||||
/**
|
||||
***********************************************************************************************************************
|
||||
* @brief Vector container.
|
||||
*
|
||||
* Vector is a templated array based storage that starts with a default-size allocation in the stack. If more space is
|
||||
* needed it then resorts to dynamic allocation by doubling the size every time the capacity is exceeded.
|
||||
* Operations which this class supports are:
|
||||
*
|
||||
* - Insertion at the end of the array.
|
||||
* - Forward iteration.
|
||||
* - Random access.
|
||||
*
|
||||
* @warning This class is not thread-safe.
|
||||
***********************************************************************************************************************
|
||||
*/
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
class Vector
|
||||
{
|
||||
public:
|
||||
/// A convenient shorthand for VectorIterator.
|
||||
typedef VectorIterator<T, defaultCapacity, Allocator> Iter;
|
||||
|
||||
/// When this allocates, it doubles the old size of memory
|
||||
static constexpr uint32 GrowthFactor = 2;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// @param [in] pAllocator The allocator that will allocate memory if required.
|
||||
Vector(Allocator*const pAllocator);
|
||||
|
||||
/// Destructor.
|
||||
~Vector();
|
||||
|
||||
/// Move constructor.
|
||||
///
|
||||
/// @param [in] vector Reference to a dying vector, from which resources will be stolen.
|
||||
Vector(Vector&& vector);
|
||||
|
||||
/// Increases maximal capacity to value greater or equal to the newCapacity.
|
||||
/// If newCapacity is greater than the maximal capacity, new storage is allocated,
|
||||
/// otherwise the method does nothing.
|
||||
///
|
||||
/// @note All existing iterators will not get invalidated, even in case new storage is allocated,
|
||||
/// because iterators are referencing vector, rather than elements of that vector.
|
||||
///
|
||||
/// @warning All pointers and references to elements of a vector will be invalidated,
|
||||
/// in case new storage is allocated.
|
||||
///
|
||||
/// @param [in] newCapacity The new capacity of a vector, which is lower limit of the maximal capacity.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result Reserve(uint32 newCapacity);
|
||||
|
||||
/// Increases maximum capacity to the number of elements in the vector, plus the specified increment amount.
|
||||
/// Equivalent to this->Reserve(this->NumElements() + amount);
|
||||
///
|
||||
/// @param [in] amount Number of items beyond the current element count to increas the capacity to.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result Grow(uint32 amount) { return Reserve(NumElements() + amount); }
|
||||
|
||||
/// Set size to newSize.
|
||||
/// If size is decreased, elements at the end of the vector will be removed.
|
||||
/// If size is increased, new elements will be set to newVal.
|
||||
/// If newSize requires a higher capacity, a new allocation is made. See notes on Reserve.
|
||||
///
|
||||
/// @note If size is decreased, any iterators referencing removed elements will become invalid. All other
|
||||
/// iterators will remain valid. Otherwise, all iterators will still be valid.
|
||||
///
|
||||
/// @warning All pointers and references to elements of a vector will be invalidated,
|
||||
/// in case new storage is allocated.
|
||||
///
|
||||
/// @param [in] newSize The new size of a vector.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result Resize(uint32 newSize, const T& newVal = T());
|
||||
|
||||
/// Copy/Move an element to end of the vector. If not enough space is available, new space will be allocated and
|
||||
/// the old data will be copied to the new space.
|
||||
///
|
||||
/// @param [in] data The element to be pushed to the vector. The element will become the last element.
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
Result PushBack(const T& data);
|
||||
Result PushBack(T&& data);
|
||||
|
||||
/// Constructs an object in-place at the end of the vector. If not enough space is available, new space will be
|
||||
/// allocated and the old data will be copied to the new space.
|
||||
///
|
||||
/// @param [in] args... The arguments passed to the constructor
|
||||
///
|
||||
/// @returns Result ErrorOutOfMemory if the operation failed.
|
||||
template <typename... Args>
|
||||
Result EmplaceBack(Args&&... args);
|
||||
|
||||
/// Returns the element at the end of the vector and destroys it.
|
||||
///
|
||||
/// @param [out] pData The element at the end of the vector.
|
||||
/// It is expected that pData is uninitialized as it will be overwritten and not destructed.
|
||||
void PopBack(T* pData);
|
||||
|
||||
/// Destroys all elements stored in the vector. All dynamically allocated memory will be saved for reuse.
|
||||
void Clear();
|
||||
|
||||
///@{
|
||||
/// Returns the element at the location specified.
|
||||
///
|
||||
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
|
||||
///
|
||||
/// @param [in] index Integer location of the element needed.
|
||||
///
|
||||
/// @returns The element at location specified by index by reference
|
||||
T& At(uint32 index)
|
||||
{
|
||||
PAL_ASSERT(index < m_numElements);
|
||||
return *(m_pData + index);
|
||||
}
|
||||
|
||||
const T& At(uint32 index) const
|
||||
{
|
||||
PAL_ASSERT(index < m_numElements);
|
||||
return *(m_pData + index);
|
||||
}
|
||||
|
||||
T& operator[](uint32 index) noexcept { return At(index); }
|
||||
const T& operator[](uint32 index) const noexcept { return At(index); }
|
||||
///@}
|
||||
|
||||
/// Returns the data at the front of the vector.
|
||||
///
|
||||
/// @warning Calling this function on an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the front of the vector.
|
||||
T& Front() const
|
||||
{
|
||||
PAL_ASSERT(IsEmpty() == false);
|
||||
return *m_pData;
|
||||
}
|
||||
|
||||
/// Returns the data at the back of the vector.
|
||||
///
|
||||
/// @warning Calling this function on an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns The data at the back of the vector.
|
||||
T& Back() const
|
||||
{
|
||||
PAL_ASSERT(IsEmpty() == false);
|
||||
return *(m_pData + (m_numElements - 1));
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first element of the vector.
|
||||
///
|
||||
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns An iterator to first element of the vector.
|
||||
Iter Begin() const { return Iter(0, *this); }
|
||||
|
||||
/// Returns an iterator to the last element of the vector.
|
||||
///
|
||||
/// @warning Accessing an element using an iterator of an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns VectorIterator An iterator to last element of the vector.
|
||||
Iter End() const { return Iter((m_numElements - 1), *this); }
|
||||
|
||||
///@{
|
||||
/// Implicitly gets the current contents of the vector as a Span.
|
||||
///
|
||||
/// @returns The contents of the vector as a Span; same as Span<T>(Data(), NumElements()).
|
||||
operator Span<T>() { return Span<T>(Data(), NumElements()); }
|
||||
operator Span<const T>() const { return Span<const T>(Data(), NumElements()); }
|
||||
///@}
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + NumElements()),
|
||||
/// even if the container is empty (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @warning Dereferencing pointer returned by Data() from an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read & write access.
|
||||
/// For a non-empty vector, the returned pointer contains address of the first element.
|
||||
/// For an empty vector, the returned pointer may or may not be a null pointer.
|
||||
T* Data() { return m_pData; }
|
||||
|
||||
/// Returns pointer to the underlying buffer serving as data storage.
|
||||
/// The returned pointer defines always valid range [Data(), Data() + NumElements()),
|
||||
/// even if the container is empty (Data() is not dereferenceable in that case).
|
||||
///
|
||||
/// @warning Dereferencing pointer returned by Data() from an empty vector will cause an access violation!
|
||||
///
|
||||
/// @returns Pointer to the underlying data storage for read only access.
|
||||
/// For a non-empty vector, the returned pointer contains address of the first element.
|
||||
/// For an empty vector, the returned pointer may or may not be a null pointer.
|
||||
const T* Data() const { return m_pData; }
|
||||
|
||||
/// Returns the size of the vector.
|
||||
///
|
||||
/// @returns An unsigned integer equal to the number of elements currently present in the vector.
|
||||
uint32 NumElements() const { return m_numElements; }
|
||||
|
||||
/// Returns true if the number of elements present in the vector is equal to zero.
|
||||
///
|
||||
/// @returns True if the vector is empty.
|
||||
bool IsEmpty() const { return (m_numElements == 0); }
|
||||
|
||||
/// Returns a pointer to the allocator used for this container's memory management.
|
||||
///
|
||||
/// @returns Allocator pointer.
|
||||
Allocator* GetAllocator() const { return m_pAllocator; }
|
||||
|
||||
///@{
|
||||
/// @internal Satisfies concept `range_expression`, using T* as `iterator` and 32-bit size and difference types
|
||||
///
|
||||
/// @note - These are a convenience intended to be used by c++ language features such as `range for`.
|
||||
/// These should not be called directly as they do not adhere to PAL coding standards.
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
using difference_type = int32;
|
||||
using size_type = uint32;
|
||||
|
||||
iterator begin() noexcept { return m_pData; }
|
||||
iterator end() noexcept { return (m_pData + m_numElements); }
|
||||
const_iterator begin() const noexcept { return m_pData; }
|
||||
const_iterator end() const noexcept { return (m_pData + m_numElements); }
|
||||
const_iterator cbegin() const noexcept { return m_pData; }
|
||||
const_iterator cend() const noexcept { return (m_pData + m_numElements); }
|
||||
[[nodiscard]] bool empty() const noexcept { return IsEmpty(); }
|
||||
size_type size() const noexcept { return m_numElements; }
|
||||
///@}
|
||||
|
||||
/// Erases the element at the specified iterator.
|
||||
void Erase(Iter it);
|
||||
|
||||
/// Erases the element at the specified iterator.
|
||||
void Erase(iterator it);
|
||||
|
||||
/// Erases the element at the specified index.
|
||||
void Erase(uint32 index);
|
||||
|
||||
/// Erase the element at the specified iterator, and swap last element to that position.
|
||||
/// If the element to erase is the last element, erase directly and no swap operation.
|
||||
void EraseAndSwapLast(Iter it);
|
||||
|
||||
/// Erase the element at the specified iterator, and swap last element to that position.
|
||||
/// If the element to erase is the last element, erase directly and no swap operation.
|
||||
void EraseAndSwapLast(iterator it);
|
||||
|
||||
/// Erases the element at the specified index, and swap last element to that position.
|
||||
/// If the element to erase is the last element, erase directly and no swap operation.
|
||||
void EraseAndSwapLast(uint32 index);
|
||||
|
||||
private:
|
||||
// This is a POD-type that exactly fits one T value.
|
||||
typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type ValueStorage;
|
||||
|
||||
ValueStorage m_data[defaultCapacity]; // The initial data buffer stored within the vector object.
|
||||
T* m_pData; // Pointer to the current data buffer.
|
||||
uint32 m_numElements; // Number of elements present.
|
||||
uint32 m_maxCapacity; // Maximum size it can hold.
|
||||
Allocator*const m_pAllocator; // Allocator for this Vector.
|
||||
|
||||
PAL_DISALLOW_COPY_AND_ASSIGN(Vector);
|
||||
|
||||
// Although this is a transgression of coding standards, it prevents VectorIterator requiring a public constructor;
|
||||
// constructing a 'bare' VectorIterator (i.e. without calling Vector::GetIterator) can never be a legal operation,
|
||||
// so this means that these two classes are much safer to use.
|
||||
friend class VectorIterator<T, defaultCapacity, Allocator>;
|
||||
};
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
VectorIterator<T, defaultCapacity, Allocator>::VectorIterator(
|
||||
uint32 index,
|
||||
const Vector<T, defaultCapacity, Allocator>& srcVec)
|
||||
:
|
||||
m_curIndex(index),
|
||||
m_srcVector(srcVec)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
Vector<T, defaultCapacity, Allocator>::Vector(
|
||||
Allocator*const pAllocator)
|
||||
:
|
||||
m_pData(reinterpret_cast<T*>(m_data)),
|
||||
m_numElements(0),
|
||||
m_maxCapacity(defaultCapacity),
|
||||
m_pAllocator(pAllocator)
|
||||
{
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
Vector<T, defaultCapacity, Allocator>::~Vector()
|
||||
{
|
||||
// Explicitly destroy all non-trivial types.
|
||||
if (!std::is_trivial<T>::value)
|
||||
{
|
||||
for (uint32 idx = 0; idx < m_numElements; ++idx)
|
||||
{
|
||||
m_pData[idx].~T();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have dynamically allocated memory.
|
||||
if (m_pData != reinterpret_cast<T*>(m_data))
|
||||
{
|
||||
// Free the memory that was allocated dynamically.
|
||||
PAL_FREE(m_pData, m_pAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================================
|
||||
// Steals allocation from a dying vector, if data buffer uses storage from heap allocation.
|
||||
// Moves objects between local buffers of new and dying vectors (for non-trivial types) or
|
||||
// copies local buffer from a dying vector to a new vector (for trivial types),
|
||||
// if data buffer uses storage from local buffer.
|
||||
template<typename T, uint32 defaultCapacity, typename Allocator>
|
||||
Vector<T, defaultCapacity, Allocator>::Vector(
|
||||
Vector&& vector)
|
||||
:
|
||||
m_numElements(vector.m_numElements),
|
||||
m_maxCapacity(vector.m_maxCapacity),
|
||||
m_pAllocator(vector.m_pAllocator)
|
||||
{
|
||||
if (vector.m_pData == reinterpret_cast<T*>(vector.m_data)) // Local buffer
|
||||
{
|
||||
// Data buffer will be using storage from local buffer.
|
||||
m_pData = reinterpret_cast<T*>(m_data);
|
||||
|
||||
if (std::is_trivial<T>::value)
|
||||
{
|
||||
// Optimize trivial types by copying local buffer.
|
||||
std::memcpy(m_pData, vector.m_pData, sizeof(T) * m_numElements);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move objects from local buffer of a dying vector to local buffer of a new vector.
|
||||
for (uint32 idx = 0; idx < m_numElements; ++idx)
|
||||
{
|
||||
PAL_PLACEMENT_NEW(m_pData + idx) T(Move(vector.m_pData[idx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Heap allocation
|
||||
{
|
||||
// Steal heap allocation from dying vector.
|
||||
m_pData = vector.m_pData;
|
||||
|
||||
// After the allocation has been stolen, dying vector is just an empty shell.
|
||||
vector.m_pData = nullptr;
|
||||
vector.m_numElements = 0;
|
||||
vector.m_maxCapacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // Util
|
||||
|
||||
Reference in New Issue
Block a user