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:
systems-assistant[bot]
2025-11-05 21:13:32 +01:00
committed by GitHub
parent 280cda3196
commit 321e497048
136 changed files with 44376 additions and 44160 deletions
@@ -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