carb/Defines.h

File members: carb/Defines.h

// Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "detail/PushBadMacros.h"

#include <cassert>
#include <cinttypes>
#include <climits>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <csignal>
#ifndef CARB_NO_MALLOC_FREE
#    include <cstring>
#else
#    include <cstddef> // for size_t
#endif
#include <new>
#include <exception> // for std::terminate
#include <type_traits>
#include <mutex>

#include "detail/PopBadMacros.h"

#define CARB_UNSUPPORTED_PLATFORM() static_assert(false, "Unsupported platform!")

#define CARB_UNSUPPORTED_ARCHITECTURE() static_assert(false, "Unsupported architecture!")

#ifndef CARB_DEBUG
#    if defined(NDEBUG) || defined(DOXYGEN_BUILD)
#        define CARB_DEBUG 0
#    else
#        define CARB_DEBUG 1
#    endif
#endif

#define CARB_VIZ

#ifdef DOXYGEN_BUILD
#    define CARB_PLATFORM_WINDOWS 0
#    define CARB_PLATFORM_LINUX 1
#    define CARB_PLATFORM_MACOS 0
#    define CARB_PLATFORM_NAME
#elif defined(CARB_PLATFORM_WINDOWS) && defined(CARB_PLATFORM_LINUX) && defined(CARB_PLATFORM_MACOS)
#    if (!!CARB_PLATFORM_WINDOWS) + (!!CARB_PLATFORM_LINUX) + (!!CARB_PLATFORM_MACOS) != 1
#        define CARB_PLATFORM_WINDOWS // show previous definition
#        define CARB_PLATFORM_LINUX // show previous definition
#        define CARB_PLATFORM_MACOS // show previous definition
#        error Exactly one of CARB_PLATFORM_WINDOWS, CARB_PLATFORM_LINUX or CARB_PLATFORM_MACOS must be non-zero.
#    endif
#elif !defined(CARB_PLATFORM_WINDOWS) && !defined(CARB_PLATFORM_LINUX)
#    ifdef _WIN32
#        define CARB_PLATFORM_WINDOWS 1
#        define CARB_PLATFORM_LINUX 0
#        define CARB_PLATFORM_MACOS 0
#        define CARB_PLATFORM_NAME "windows"
#    elif defined(__linux__)
#        define CARB_PLATFORM_WINDOWS 0
#        define CARB_PLATFORM_LINUX 1
#        define CARB_PLATFORM_MACOS 0
#        define CARB_PLATFORM_NAME "linux"
#    elif defined(__APPLE__)
#        define CARB_PLATFORM_WINDOWS 0
#        define CARB_PLATFORM_LINUX 0
#        define CARB_PLATFORM_MACOS 1
#        define CARB_PLATFORM_NAME "macos"
#    else
CARB_UNSUPPORTED_PLATFORM();
#    endif
#else
#    error "Must define all of CARB_PLATFORM_WINDOWS, CARB_PLATFORM_LINUX and CARB_PLATFORM_MACOS or none."
#endif

#if CARB_PLATFORM_LINUX || CARB_PLATFORM_MACOS || defined(DOXYGEN_BUILD)
#    include <unistd.h> // _POSIX_VERSION comes from unistd.h

#    define CARB_POSIX _POSIX_VERSION
#else
#    define CARB_POSIX 0
#endif

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#    if CARB_PLATFORM_WINDOWS
#        ifndef CARB_NO_MALLOC_FREE
#            include "malloc.h"
#        endif
#        include <intrin.h>
#    elif CARB_PLATFORM_LINUX
#        include <alloca.h>
#        include <signal.h>
#        define _alloca alloca
#    endif
#endif

// Architecture defines
#ifdef DOXYGEN_BUILD
#    define CARB_AARCH64 0
#    define CARB_X86_64 1
#    define CARB_ARCH_NAME
#elif defined(__aarch64__)
#    define CARB_AARCH64 1
#    define CARB_X86_64 0
#elif defined(__x86_64__) /*GCC*/ || defined(_M_X64) /*MSVC*/
#    define CARB_X86_64 1
#    define CARB_AARCH64 0
#endif

#if CARB_PLATFORM_MACOS
#    define CARB_ARCH_NAME "universal"
#else
#    if CARB_X86_64
#        define CARB_ARCH_NAME "x86_64"
#    elif CARB_AARCH64
#        define CARB_ARCH_NAME "aarch64"
#    endif
#endif

#ifndef CARB_PROFILING
#    define CARB_PROFILING 1
#endif

#ifdef DOXYGEN_BUILD
#    define CARB_TEGRA 0
#elif !defined(CARB_TEGRA)
#    if defined(__aarch64__) && defined(__LINARO_RELEASE__)
#        define CARB_TEGRA 1
#    else
#        define CARB_TEGRA 0
#    endif
#endif

#ifdef DOXYGEN_BUILD
#    define CARB_COMPILER_MSC 0
#    define CARB_COMPILER_GNUC 1
#elif defined(CARB_COMPILER_MSC) && defined(CARB_COMPILER_GNUC)
#    if (!!CARB_COMPILER_MSC) + (!!CARB_COMPILER_GNUC) != 1
#        define CARB_COMPILER_MSC // Show previous definition
#        define CARB_COMPILER_GNUC // Show previous definition
#        error Exactly one of CARB_COMPILER_MSC or CARB_COMPILER_GNUC must be non-zero.
#    endif
#elif !defined(CARB_COMPILER_MSC) && !defined(CARB_COMPILER_GNUC)
#    ifndef CARB_COMPILER_MSC
#        if defined(_MSC_VER)
#            define CARB_COMPILER_MSC 1
#            define CARB_COMPILER_GNUC 0
#        elif defined(__GNUC__)
#            define CARB_COMPILER_MSC 0
#            define CARB_COMPILER_GNUC 1
#        else
#            error "Unsupported compiler."
#        endif
#    endif
#else
#    error "Must define CARB_COMPILER_MSC and CARB_COMPILER_GNUC or neither."
#endif

#ifdef DOXYGEN_BUILD
#    define CARB_TOOLCHAIN_CLANG 0
#elif !defined(CARB_TOOLCHAIN_CLANG)
#    if defined(__clang__)
#        define CARB_TOOLCHAIN_CLANG 1
#    else
#        define CARB_TOOLCHAIN_CLANG 0
#    endif
#endif

#ifdef DOXYGEN_BUILD
#    define CARB_ASAN_ENABLED 0
#elif !defined(CARB_ASAN_ENABLED)
#    ifdef __SANITIZE_ADDRESS__
#        define CARB_ASAN_ENABLED __SANITIZE_ADDRESS__
#    else
#        define CARB_ASAN_ENABLED 0
#    endif
#endif

#ifdef DOXYGEN_BUILD
#    define CARB_TSAN_ENABLED 0
#elif !defined(CARB_TSAN_ENABLED)
#    ifdef __SANITIZE_THREAD__
#        define CARB_TSAN_ENABLED __SANITIZE_THREAD__
#    else
#        define CARB_TSAN_ENABLED 0
#    endif
#endif

#define CARB_DEPAREN(pack_) CARB_IDENTITY pack_

#define CARB_IDENTITY(...) __VA_ARGS__

// Compiler specific defines. Exist for all supported compilers but may be a no-op for certain compilers.
#ifdef DOXYGEN_BUILD
#    define CARB_PRETTY_FUNCTION "<function signature here>"
#    define CARB_ATTRIBUTE(...)
#    define CARB_DECLSPEC(...)
#    define CARB_MSC_ONLY(...)
#    define CARB_NOT_MSC(...)
#    define CARB_GNUC_ONLY(...)
#    define CARB_NOT_GNUC(...)
#    define CARB_PRAGMA(...)
#    define CARB_PRAGMA_MSC(...)
#    define CARB_PRAGMA_GNUC(...)

#    define CARB_DOC_CONSTEXPR const

#    define CARB_EXCEPTIONS_ENABLED 1

#    define CARB_DOC_ONLY(...) __VA_ARGS__

#    define CARB_NO_DOC(...)
#else
#    define CARB_DOC_CONSTEXPR constexpr
#    define CARB_DOC_ONLY(...)
#    define CARB_NO_DOC(...) __VA_ARGS__
#    if CARB_COMPILER_MSC
#        define CARB_PRETTY_FUNCTION __FUNCSIG__
#        define CARB_ATTRIBUTE(...)
#        define CARB_MSC_ONLY(...) __VA_ARGS__
#        define CARB_NOT_MSC(...)
#        define CARB_GNUC_ONLY(...)
#        define CARB_NOT_GNUC(...) __VA_ARGS__
#        define CARB_PRAGMA(...) __pragma(__VA_ARGS__)
#        define CARB_DECLSPEC(...) __declspec(__VA_ARGS__)
#        define CARB_PRAGMA_MSC(...) CARB_PRAGMA(__VA_ARGS__)
#        define CARB_PRAGMA_GNUC(...)

#        ifdef __cpp_exceptions
#            define CARB_EXCEPTIONS_ENABLED 1
#        else
#            define CARB_EXCEPTIONS_ENABLED 0
#        endif
// Other MSC-specific definitions that must exist outside of the carb namespace
extern "C" void _mm_prefetch(char const* _A, int _Sel); // From winnt.h/intrin.h

#        if defined(__INTELLISENSE__) && _MSC_VER < 1920
// See: https://stackoverflow.com/questions/61485127/including-windows-h-causes-unknown-attributeno-init-all-error
#            define no_init_all deprecated
#        endif

#    elif CARB_COMPILER_GNUC
#        define CARB_PRETTY_FUNCTION __PRETTY_FUNCTION__
#        define CARB_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
#        define CARB_DECLSPEC(...)
#        define CARB_MSC_ONLY(...)
#        define CARB_NOT_MSC(...) __VA_ARGS__
#        define CARB_GNUC_ONLY(...) __VA_ARGS__
#        define CARB_NOT_GNUC(...)
#        define CARB_PRAGMA(...) _Pragma(__VA_ARGS__)
#        define CARB_PRAGMA_MSC(...)
#        define CARB_PRAGMA_GNUC(...) CARB_PRAGMA(__VA_ARGS__)

#        ifdef __EXCEPTIONS
#            define CARB_EXCEPTIONS_ENABLED 1
#        else
#            define CARB_EXCEPTIONS_ENABLED 0
#        endif
#    else
#        error Unsupported compiler
#    endif
#endif

#if defined(DOXYGEN_BUILD) || defined(OMNI_BIND)
#    define CARB_OPTIMIZE_OFF_MSC()
#    define CARB_OPTIMIZE_ON_MSC()
#    define CARB_NO_OPTIMIZE_GNUC_CLANG()
#else
#    if CARB_COMPILER_MSC
#        define CARB_OPTIMIZE_OFF_MSC() CARB_PRAGMA_MSC(optimize("", off))
#        define CARB_OPTIMIZE_ON_MSC() CARB_PRAGMA_MSC(optimize("", on))
#        define CARB_NO_OPTIMIZE_GNUC_CLANG()
#    elif CARB_TOOLCHAIN_CLANG
#        define CARB_NO_OPTIMIZE_GNUC_CLANG() CARB_ATTRIBUTE(optnone)
#        define CARB_OPTIMIZE_OFF_MSC()
#        define CARB_OPTIMIZE_ON_MSC()
#    elif CARB_COMPILER_GNUC
#        define CARB_NO_OPTIMIZE_GNUC_CLANG() CARB_ATTRIBUTE(optimize("-O0"))
#        define CARB_OPTIMIZE_OFF_MSC()
#        define CARB_OPTIMIZE_ON_MSC()
#    else
#        error Unsupported compiler
#    endif
#endif

// MSC-specific warning macros are defined only for MSC
// CARB_IGNOREWARNING_MSC_PUSH: MSVC only; pushes the warning state
// CARB_IGNOREWARNING_MSC_POP: MSVC only; pops the warning state
// CARB_IGNOREWARNING_MSC(w): MSVC only; disables the given warning number (ex: CARB_IGNOREWARNING_MSC(4505))
// CARB_IGNOREWARNING_MSC_WITH_PUSH(w): MSVC only; combines CARB_IGNOREWARNING_MSC_PUSH and CARB_IGNOREWARNING_MSC()
#if !defined(DOXYGEN_BUILD) && CARB_COMPILER_MSC
#    define CARB_IGNOREWARNING_MSC_PUSH __pragma(warning(push))
#    define CARB_IGNOREWARNING_MSC_POP __pragma(warning(pop))
#    define CARB_IGNOREWARNING_MSC(w) __pragma(warning(disable : w))
#    define CARB_IGNOREWARNING_MSC_WITH_PUSH(w)                                                                        \
        CARB_IGNOREWARNING_MSC_PUSH                                                                                    \
        CARB_IGNOREWARNING_MSC(w)
#else
#    define CARB_IGNOREWARNING_MSC_PUSH
#    define CARB_IGNOREWARNING_MSC_POP
#    define CARB_IGNOREWARNING_MSC(w)
#    define CARB_IGNOREWARNING_MSC_WITH_PUSH(w)
#endif

// GNUC-specific helper macros are defined for GCC and Clang-infrastructure
// CARB_IGNOREWARNING_GNUC_PUSH: GCC only; pushes the warning state
// CARB_IGNOREWARNING_GNUC_POP: GCC only; pops the warning state
// CARB_IGNOREWARNING_CLANG_PUSH: Clang only; pushes the warning state
// CARB_IGNOREWARNING_CLANG_POP: Clang only; pops the warning state
// CARB_IGNOREWARNING_GNUC(w): GCC only; disables the given warning (ex: CARB_IGNOREWARNING_GNUC("-Wattributes"))
// CARB_IGNOREWARNING_GNUC_WITH_PUSH(w): GCC only; combines CARB_IGNOREWARNING_GNUC_PUSH and CARB_IGNOREWARNING_GNUC()
// CARB_IGNOREWARNING_CLANG(w): Clang only; disables the given warning (ex: CARB_IGNOREWARNING_CLANG("-Wattributes"))
// CARB_IGNOREWARNING_CLANG_WITH_PUSH(w): Clang only; combines CARB_IGNOREWARNING_CLANG_PUSH and
// CARB_IGNOREWARNING_CLANG()
#if !defined(DOXYGEN_BUILD) && (CARB_COMPILER_GNUC || CARB_TOOLCHAIN_CLANG)
#    define CARB_IGNOREWARNING_GNUC_PUSH _Pragma("GCC diagnostic push")
#    define CARB_IGNOREWARNING_GNUC_POP _Pragma("GCC diagnostic pop")
#    define INTERNAL_CARB_IGNOREWARNING_GNUC(str) _Pragma(#    str)
#    define CARB_IGNOREWARNING_GNUC(w) INTERNAL_CARB_IGNOREWARNING_GNUC(GCC diagnostic ignored w)
#    define CARB_IGNOREWARNING_GNUC_WITH_PUSH(w) CARB_IGNOREWARNING_GNUC_PUSH CARB_IGNOREWARNING_GNUC(w)
#    if CARB_TOOLCHAIN_CLANG
#        define CARB_IGNOREWARNING_CLANG_PUSH _Pragma("GCC diagnostic push")
#        define CARB_IGNOREWARNING_CLANG_POP _Pragma("GCC diagnostic pop")
#        define INTERNAL_CARB_IGNOREWARNING_CLANG(str) _Pragma(#        str)
#        define CARB_IGNOREWARNING_CLANG(w) INTERNAL_CARB_IGNOREWARNING_CLANG(GCC diagnostic ignored w)
#        define CARB_IGNOREWARNING_CLANG_WITH_PUSH(w) CARB_IGNOREWARNING_CLANG_PUSH CARB_IGNOREWARNING_CLANG(w)
#    else
#        define CARB_IGNOREWARNING_CLANG_PUSH
#        define CARB_IGNOREWARNING_CLANG_POP
#        define CARB_IGNOREWARNING_CLANG(w)
#        define CARB_IGNOREWARNING_CLANG_WITH_PUSH(w)
#    endif
#else
#    define CARB_IGNOREWARNING_GNUC_PUSH
#    define CARB_IGNOREWARNING_GNUC_POP
#    define CARB_IGNOREWARNING_CLANG_PUSH
#    define CARB_IGNOREWARNING_CLANG_POP
#    define CARB_IGNOREWARNING_GNUC(w)
#    define CARB_IGNOREWARNING_GNUC_WITH_PUSH(w)
#    define CARB_IGNOREWARNING_CLANG(w)
#    define CARB_IGNOREWARNING_CLANG_WITH_PUSH(w)
#endif

#if defined(__cplusplus) || defined(DOXYGEN_BUILD)
#    define CARB_EXTERN_C extern "C"
#else
#    define CARB_EXTERN_C
#endif

#define CARB_EXPORT CARB_EXTERN_C CARB_DECLSPEC(dllexport) CARB_ATTRIBUTE(visibility("default"))

#define CARB_IMPORT CARB_EXTERN_C

// For documentation only
#ifdef DOXYGEN_BUILD
#    define CARB_EXPORTS
#endif

#if defined(CARB_EXPORTS) || defined(DOXYGEN_BUILD)
#    define CARB_DYNAMICLINK CARB_EXPORT
#else
#    define CARB_DYNAMICLINK CARB_IMPORT
#endif

#if CARB_PLATFORM_WINDOWS || defined(DOXYGEN_BUILD)
#    define CARB_ABI __cdecl
#else
#    define CARB_ABI
#endif

#if (defined(__cplusplus) && __cplusplus >= 201400L) || defined(DOXYGEN_BUILD)
#    define CARB_HAS_CPP14 1
#else
#    define CARB_HAS_CPP14 0
#endif

#if (defined(__cplusplus) && __cplusplus >= 201700L) || defined(DOXYGEN_BUILD)
#    define CARB_HAS_CPP17 1
#else
#    define CARB_HAS_CPP17 0
#endif

#if CARB_HAS_CPP17 && !defined(DOXYGEN_BUILD)
#    define CARB_CPP17_CONSTEXPR constexpr
#else
#    define CARB_CPP17_CONSTEXPR
#endif

#if (defined(__cplusplus) && __cplusplus >= 202000L) || defined(DOXYGEN_BUILD)
#    define CARB_HAS_CPP20 1
#else
#    define CARB_HAS_CPP20 0
#endif

#if defined(__has_cpp_attribute) // C++20
#    define CARBLOCAL_HAS_NODISCARD (__has_cpp_attribute(nodiscard) >= 201603L)
#    define CARBLOCAL_HAS_NODISCARD_MSG (__has_cpp_attribute(nodiscard) >= 201907L)
#    define CARBLOCAL_HAS_FALLTHROUGH (__has_cpp_attribute(fallthrough) >= 201603L)
#    define CARBLOCAL_HAS_MAYBE_UNUSED (__has_cpp_attribute(maybe_unused) >= 201603L)
#    define CARBLOCAL_HAS_LIKELY (__has_cpp_attribute(likely) >= 201803L)
#    define CARBLOCAL_HAS_UNLIKELY (__has_cpp_attribute(unlikely) >= 201803L)
#    define CARBLOCAL_HAS_NO_UNIQUE_ADDRESS (__has_cpp_attribute(no_unique_address) >= 201803L)
#else
#    define CARBLOCAL_HAS_NODISCARD CARB_HAS_CPP17
#    define CARBLOCAL_HAS_NODISCARD_MSG 0
#    define CARBLOCAL_HAS_FALLTHROUGH CARB_HAS_CPP17
#    define CARBLOCAL_HAS_MAYBE_UNUSED CARB_HAS_CPP17
#    define CARBLOCAL_HAS_LIKELY 0
#    define CARBLOCAL_HAS_UNLIKELY 0
#    define CARBLOCAL_HAS_NO_UNIQUE_ADDRESS 0
#endif

// [[nodiscard]]
#if (CARB_HAS_CPP17 && CARBLOCAL_HAS_NODISCARD) || defined(DOXYGEN_BUILD)
#    define CARB_NODISCARD [[nodiscard]]
#    define CARB_NODISCARD_TYPE [[nodiscard]]
#elif CARB_COMPILER_GNUC
#    define CARB_NODISCARD __attribute__((warn_unused_result))
#    define CARB_NODISCARD_TYPE
#else // not supported
#    define CARB_NODISCARD
#    define CARB_NODISCARD_TYPE
#endif

// [[nodiscard(msg)]]
#if (CARB_HAS_CPP20 && CARBLOCAL_HAS_NODISCARD_MSG) || defined(DOXYGEN_BUILD)
#    define CARB_NODISCARD_MSG(msg) [[nodiscard(msg)]]
#    define CARB_NODISCARD_TYPE_MSG(msg) [[nodiscard(msg)]]
#else
#    define CARB_NODISCARD_MSG(msg) CARB_NODISCARD
#    define CARB_NODISCARD_TYPE_MSG(msg) CARB_NODISCARD_TYPE
#endif

// [[fallthrough]]
#if (CARB_HAS_CPP17 && CARBLOCAL_HAS_FALLTHROUGH) || defined(DOXYGEN_BUILD)
#    define CARB_FALLTHROUGH [[fallthrough]]
#elif CARB_COMPILER_GNUC
#    if __GNUC__ >= 7
#        define CARB_FALLTHROUGH __attribute__((fallthrough))
#    else
// Marker comment
#        define CARB_FALLTHROUGH /* fall through */
#    endif
#else // not supported
#    define CARB_FALLTHROUGH
#endif

// [[maybe_unused]]
#if (CARB_HAS_CPP17 && CARBLOCAL_HAS_MAYBE_UNUSED) || defined(DOXYGEN_BUILD)
#    define CARB_MAYBE_UNUSED [[maybe_unused]]
#elif CARB_COMPILER_GNUC
#    define CARB_MAYBE_UNUSED __attribute__((unused))
#else // not supported
#    define CARB_MAYBE_UNUSED
#endif

// [[likely]] / [[unlikely]]
#if (CARB_HAS_CPP20 && CARBLOCAL_HAS_LIKELY) || defined(DOXYGEN_BUILD)
#    define CARB_CPP20_LIKELY [[likely]]
#else
#    define CARB_CPP20_LIKELY
#endif

#if (CARB_HAS_CPP20 && CARBLOCAL_HAS_UNLIKELY) || defined(DOXYGEN_BUILD)
#    define CARB_CPP20_UNLIKELY [[unlikely]]
#else
#    define CARB_CPP20_UNLIKELY
#endif

#if CARB_COMPILER_GNUC || defined(DOXYGEN_BUILD)
#    define CARB_LIKELY(expr) __builtin_expect(!!(expr), 1)
#    define CARB_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
#else // not supported
#    define CARB_LIKELY(expr) (!!(expr))
#    define CARB_UNLIKELY(expr) (!!(expr))
#endif

#define CARB_LIKELY_IF(...)                                                                                            \
    if (CARB_LIKELY(__VA_ARGS__))                                                                                      \
    CARB_CPP20_LIKELY

#define CARB_UNLIKELY_IF(...)                                                                                          \
    if (CARB_UNLIKELY(__VA_ARGS__))                                                                                    \
    CARB_CPP20_UNLIKELY

// [[no_unique_address]]
#if (CARB_HAS_CPP20 && CARBLOCAL_HAS_NO_UNIQUE_ADDRESS) || defined(DOXYGEN_BUILD)
#    define CARB_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else // not supported
#    define CARB_NO_UNIQUE_ADDRESS
#endif

#define CARB_HIDDEN CARB_ATTRIBUTE(visibility("hidden"))

#define CARB_WEAKLINK CARB_DECLSPEC(selectany) CARB_ATTRIBUTE(weak)

// constexpr in CPP20, but not before
#if (CARB_HAS_CPP20 && __cpp_constexpr >= 201907L) || defined(DOXYGEN_BUILD)
#    define CARB_CPP20_CONSTEXPR constexpr
#else
#    define CARB_CPP20_CONSTEXPR
#endif

// include the IAssert interface here.  Note that this cannot be included any earlier because
// it requires symbols such as "CARB_ABI".  Also note that it cannot be put into the CARB_DEBUG
// section below because the mirroring tool picks it up and generates type information for it.
// If it is not unconditionally included here, that leads to build errors in release builds.
#include "assert/IAssert.h"

#ifdef DOXYGEN_BUILD
#    define CARB_BREAK_POINT()
#elif CARB_POSIX
#    define CARB_BREAK_POINT() ::raise(SIGTRAP)
#elif CARB_PLATFORM_WINDOWS
#    define CARB_BREAK_POINT() ::__debugbreak()
#else
CARB_UNSUPPORTED_PLATFORM();
#endif

namespace carb
{

#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace detail
{
// clang-format off
#define C(a) (unsigned char)(0x##a)
constexpr unsigned char lowerTable[256] = {
    C(00), C(01), C(02), C(03), C(04), C(05), C(06), C(07), C(08), C(09), C(0A), C(0B), C(0C), C(0D), C(0E), C(0F),
    C(10), C(11), C(12), C(13), C(14), C(15), C(16), C(17), C(18), C(19), C(1A), C(1B), C(1C), C(1D), C(1E), C(1F),
    C(20), C(21), C(22), C(23), C(24), C(25), C(26), C(27), C(28), C(29), C(2A), C(2B), C(2C), C(2D), C(2E), C(2F),
    C(30), C(31), C(32), C(33), C(34), C(35), C(36), C(37), C(38), C(39), C(3A), C(3B), C(3C), C(3D), C(3E), C(3F),
    C(40),

           // [0x41, 0x5A] -> [0x61, 0x7A]
           C(61), C(62), C(63), C(64), C(65), C(66), C(67), C(68), C(69), C(6A), C(6B), C(6C), C(6D), C(6E), C(6F),
    C(70), C(71), C(72), C(73), C(74), C(75), C(76), C(77), C(78), C(79), C(7A),

                                                                                 C(5B), C(5C), C(5D), C(5E), C(5F),
    C(60), C(61), C(62), C(63), C(64), C(65), C(66), C(67), C(68), C(69), C(6A), C(6B), C(6C), C(6D), C(6E), C(6F),
    C(70), C(71), C(72), C(73), C(74), C(75), C(76), C(77), C(78), C(79), C(7A), C(7B), C(7C), C(7D), C(7E), C(7F),
    C(80), C(81), C(82), C(83), C(84), C(85), C(86), C(87), C(88), C(89), C(8A), C(8B), C(8C), C(8D), C(8E), C(8F),
    C(90), C(91), C(92), C(93), C(94), C(95), C(96), C(97), C(98), C(99), C(9A), C(9B), C(9C), C(9D), C(9E), C(9F),
    C(A0), C(A1), C(A2), C(A3), C(A4), C(A5), C(A6), C(A7), C(A8), C(A9), C(AA), C(AB), C(AC), C(AD), C(AE), C(AF),
    C(B0), C(B1), C(B2), C(B3), C(B4), C(B5), C(B6), C(B7), C(B8), C(B9), C(BA), C(BB), C(BC), C(BD), C(BE), C(BF),
    C(C0), C(C1), C(C2), C(C3), C(C4), C(C5), C(C6), C(C7), C(C8), C(C9), C(CA), C(CB), C(CC), C(CD), C(CE), C(CF),
    C(D0), C(D1), C(D2), C(D3), C(D4), C(D5), C(D6), C(D7), C(D8), C(D9), C(DA), C(DB), C(DC), C(DD), C(DE), C(DF),
    C(E0), C(E1), C(E2), C(E3), C(E4), C(E5), C(E6), C(E7), C(E8), C(E9), C(EA), C(EB), C(EC), C(ED), C(EE), C(EF),
    C(F0), C(F1), C(F2), C(F3), C(F4), C(F5), C(F6), C(F7), C(F8), C(F9), C(FA), C(FB), C(FC), C(FD), C(FE), C(FF),
};
constexpr unsigned char upperTable[256] = {
    C(00), C(01), C(02), C(03), C(04), C(05), C(06), C(07), C(08), C(09), C(0A), C(0B), C(0C), C(0D), C(0E), C(0F),
    C(10), C(11), C(12), C(13), C(14), C(15), C(16), C(17), C(18), C(19), C(1A), C(1B), C(1C), C(1D), C(1E), C(1F),
    C(20), C(21), C(22), C(23), C(24), C(25), C(26), C(27), C(28), C(29), C(2A), C(2B), C(2C), C(2D), C(2E), C(2F),
    C(30), C(31), C(32), C(33), C(34), C(35), C(36), C(37), C(38), C(39), C(3A), C(3B), C(3C), C(3D), C(3E), C(3F),
    C(40), C(41), C(42), C(43), C(44), C(45), C(46), C(47), C(48), C(49), C(4A), C(4B), C(4C), C(4D), C(4E), C(4F),
    C(50), C(51), C(52), C(53), C(54), C(55), C(56), C(57), C(58), C(59), C(5A), C(5B), C(5C), C(5D), C(5E), C(5F),
    C(60),

           // [0x61, 0x7A] -> [0x41, 0x5A]
           C(41), C(42), C(43), C(44), C(45), C(46), C(47), C(48), C(49), C(4A), C(4B), C(4C), C(4D), C(4E), C(4F),
    C(50), C(51), C(52), C(53), C(54), C(55), C(56), C(57), C(58), C(59), C(5A),

                                                                                 C(7B), C(7C), C(7D), C(7E), C(7F),
    C(80), C(81), C(82), C(83), C(84), C(85), C(86), C(87), C(88), C(89), C(8A), C(8B), C(8C), C(8D), C(8E), C(8F),
    C(90), C(91), C(92), C(93), C(94), C(95), C(96), C(97), C(98), C(99), C(9A), C(9B), C(9C), C(9D), C(9E), C(9F),
    C(A0), C(A1), C(A2), C(A3), C(A4), C(A5), C(A6), C(A7), C(A8), C(A9), C(AA), C(AB), C(AC), C(AD), C(AE), C(AF),
    C(B0), C(B1), C(B2), C(B3), C(B4), C(B5), C(B6), C(B7), C(B8), C(B9), C(BA), C(BB), C(BC), C(BD), C(BE), C(BF),
    C(C0), C(C1), C(C2), C(C3), C(C4), C(C5), C(C6), C(C7), C(C8), C(C9), C(CA), C(CB), C(CC), C(CD), C(CE), C(CF),
    C(D0), C(D1), C(D2), C(D3), C(D4), C(D5), C(D6), C(D7), C(D8), C(D9), C(DA), C(DB), C(DC), C(DD), C(DE), C(DF),
    C(E0), C(E1), C(E2), C(E3), C(E4), C(E5), C(E6), C(E7), C(E8), C(E9), C(EA), C(EB), C(EC), C(ED), C(EE), C(EF),
    C(F0), C(F1), C(F2), C(F3), C(F4), C(F5), C(F6), C(F7), C(F8), C(F9), C(FA), C(FB), C(FC), C(FD), C(FE), C(FF),
};
#undef C
// clang-format on
} // namespace detail
#endif

inline bool assertHandlerFallback(
    const char* condition, const char* file, const char* func, int32_t line, const char* fmt = nullptr, ...)
{
    static std::mutex m;
    std::lock_guard<std::mutex> g(m);

    if (fmt != nullptr)
    {
        fprintf(stderr, "%s:%s():%" PRId32 ": Assertion (%s) failed: ", file, func, line, condition);
        va_list args;
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
        fputc('\n', stderr);
    }
    else
        fprintf(stderr, "%s:%" PRId32 ":%s(): Assertion (%s) failed.\n", file, line, func, condition);

    return true;
}

} // namespace carb

#ifdef DOXYGEN_BUILD
#    define CARB_ASSERT_ENABLED 0
#    define CARB_CHECK_ENABLED 0
#    define CARB_ASSERT(cond, ...) ((void)0)
#    define CARB_CHECK(cond, ...) ((void)0)
#    define CARB_FATAL_UNLESS(cond, fmt, ...) (!(cond) ? (std::terminate(), false) : true)
#else
/* main assertion test entry point.  This is implemented as a single conditional statement to
 * ensure that the assertion failure breakpoint occurs on the same line of code as the assertion
 * test itself. CARB_CHECK() exists in release and debug, and CARB_ASSERT() is debug-only.
 */
// example-begin CARB_IMPL_ASSERT
#    define CARB_IMPL_ASSERT(cond, ...)                                                                                \
        (CARB_LIKELY(cond) ||                                                                                          \
         ![&](const char* funcname__, ...) CARB_NOINLINE {                                                             \
             return g_carbAssert ?                                                                                     \
                        g_carbAssert->reportFailedAssertion(#cond, __FILE__, funcname__, __LINE__, ##__VA_ARGS__) :    \
                        ::carb::assertHandlerFallback(#cond, __FILE__, funcname__, __LINE__, ##__VA_ARGS__);           \
         }(CARB_PRETTY_FUNCTION) ||                                                                                    \
         (CARB_BREAK_POINT(), false))
// example-end CARB_IMPL_ASSERT

#    ifndef CARB_CHECK
#        ifndef CARB_CHECK_ENABLED
#            define CARB_CHECK_ENABLED 1
#        endif
#        if CARB_CHECK_ENABLED
#            define CARB_CHECK(cond, ...) CARB_IMPL_ASSERT(cond, ##__VA_ARGS__)
#        else
#            define CARB_CHECK(cond, ...) ((void)0)
#        endif
#    else
// CARB_CHECK was already defined
#        ifndef CARB_CHECK_ENABLED
#            define CARB_CHECK /* cause an error showing where it was already defined */
#            error CARB_CHECK_ENABLED must also be defined if CARB_CHECK is pre-defined!
#        endif
#    endif

#    ifndef CARB_FATAL_UNLESS
// example-begin CARB_FATAL_UNLESS
#        define CARB_FATAL_UNLESS(cond, fmt, ...)                                                                      \
            (CARB_LIKELY(cond) ||                                                                                      \
            ([&](const char* funcname__, ...) CARB_NOINLINE {                                                          \
                if (false)                                                                                             \
                    ::printf(fmt, ##__VA_ARGS__);                                                                      \
                g_carbAssert ? g_carbAssert->reportFailedAssertion(#cond, __FILE__, funcname__, __LINE__, fmt, ##__VA_ARGS__) : \
                   ::carb::assertHandlerFallback(#cond, __FILE__, funcname__, __LINE__, fmt, ##__VA_ARGS__);           \
             }(CARB_PRETTY_FUNCTION), std::terminate(), false))
  // example-end CARB_FATAL_UNLESS
#    endif

#    ifndef CARB_ASSERT
#        ifndef CARB_ASSERT_ENABLED
#            if CARB_DEBUG
#                define CARB_ASSERT_ENABLED 1
#            else
#                define CARB_ASSERT_ENABLED 0
#            endif
#        endif
#        if CARB_ASSERT_ENABLED
#            define CARB_ASSERT(cond, ...) CARB_IMPL_ASSERT(cond, ##__VA_ARGS__)
#        else
#            define CARB_ASSERT(cond, ...) ((void)0)
#        endif
#    else
// CARB_ASSERT was already defined
#        ifndef CARB_ASSERT_ENABLED
#            define CARB_ASSERT /* cause an error showing where it was already defined */
#            error CARB_ASSERT_ENABLED must also be defined if CARB_ASSERT is pre-defined!
#        endif
#    endif
#endif

#define CARB_ASSERT_STRUCTS_MATCH(A, B)                                                                                \
    static_assert(                                                                                                     \
        sizeof(A) == sizeof(B) && alignof(A) == alignof(B), "Size or alignment mismatch between " #A " and " #B ".")

#define CARB_ASSERT_MEMBERS_MATCH(A, a, B, b)                                                                          \
    static_assert(offsetof(A, a) == offsetof(B, b) && sizeof(A::a) == sizeof(B::b),                                    \
                  "Offset or size mismatch between members " #a " of " #A " and " #b " of " #B ".")

#define CARB_UINT16_MAX UINT16_MAX
#define CARB_UINT32_MAX UINT32_MAX
#define CARB_UINT64_MAX UINT64_MAX
#define CARB_ULLONG_MAX ULLONG_MAX
#define CARB_USHRT_MAX USHRT_MAX
#define CARB_FLOAT_MAX 3.402823466e+38F

#define CARB_MIN(a, b) (((a) < (b)) ? (a) : (b))

#define CARB_MAX(a, b) (((a) > (b)) ? (a) : (b))

#define CARB_CLAMP(x, lo, hi) (((x) < (lo)) ? (lo) : (((x) > (hi)) ? (hi) : (x)))

#define CARB_ROUNDUP(value, to) ((((value) + (to)-1) / (to)) * (to))

#ifndef DOXYGEN_SHOULD_SKIP_THIS
// CARB_JOIN will join together `a` and `b` and also work properly if either parameter is another macro like __LINE__.
// This requires two macros since the preprocessor will only recurse macro expansion if # and ## are not present.
#    define __CARB_JOIN(a, b) a##b
#endif

#define CARB_JOIN(a, b) __CARB_JOIN(a, b)

#define CARB_PREVENT_COPY(classname)                                                                                   \
    classname(const classname&) = delete;                                                              \
    classname& operator=(const classname&) = delete
#define CARB_PREVENT_MOVE(classname)                                                                                   \
    classname(classname&&) = delete;                                                                   \
    classname& operator=(classname&&) = delete
#define CARB_PREVENT_COPY_AND_MOVE(classname)                                                                          \
    CARB_PREVENT_COPY(classname);                                                                                      \
    CARB_PREVENT_MOVE(classname)

#if defined(__COUNTER__) || defined(DOXYGEN_BUILD)
#    define CARB_ANONYMOUS_VAR(str) CARB_JOIN(str, __COUNTER__)
#else
#    define CARB_ANONYMOUS_VAR(str) CARB_JOIN(str, __LINE__)
#endif

namespace carb
{

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T, size_t N>
constexpr size_t countOf(T const (&)[N])
{
    return N;
}
#endif
#define CARB_COUNTOF(a) carb::countOf(a)

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T, uint32_t N>
constexpr uint32_t countOf32(T const (&)[N])
{
    return N;
}
#endif
#define CARB_COUNTOF32(a) carb::countOf32(a)

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T, typename U>
constexpr uint32_t offsetOf(U T::*member)
{
    CARB_IGNOREWARNING_GNUC_PUSH
#    if CARB_TOOLCHAIN_CLANG && __clang_major__ >= 13 // this error is issued on clang 13
    CARB_IGNOREWARNING_GNUC_WITH_PUSH("-Wnull-pointer-subtraction")
#    endif
    return (uint32_t)((char*)&((T*)nullptr->*member) - (char*)nullptr);
    CARB_IGNOREWARNING_GNUC_POP
}
#endif
#define CARB_OFFSETOF(a) carb::offsetOf(&a)

#if CARB_COMPILER_MSC || defined(DOXYGEN_BUILD)
#    define CARB_ALIGN_OF(T) __alignof(T)
#elif CARB_COMPILER_GNUC
#    define CARB_ALIGN_OF(T) __alignof__(T)
#else
#    error "Align of cannot be determined - compiler not known"
#endif

// Implement CARB_HARDWARE_PAUSE; a way of idling the pipelines and reducing the penalty
// from memory order violations. See
// https://software.intel.com/en-us/articles/long-duration-spin-wait-loops-on-hyper-threading-technology-enabled-intel-processors
#ifdef DOXYGEN_BUILD
#    define CARB_HARDWARE_PAUSE()
#elif CARB_X86_64
// avoid including immintrin.h
#    if CARB_COMPILER_MSC
#        pragma intrinsic(_mm_pause)
#        define CARB_HARDWARE_PAUSE() _mm_pause()
#    else
#        define CARB_HARDWARE_PAUSE() __builtin_ia32_pause()
#    endif
#elif defined(__aarch64__)
#    define CARB_HARDWARE_PAUSE() __asm__ __volatile__("yield" ::: "memory")
#else
CARB_UNSUPPORTED_PLATFORM();
#endif

#if CARB_COMPILER_MSC || defined(DOXYGEN_BUILD)
#    pragma intrinsic(_mm_prefetch)
#    define CARB_ALWAYS_INLINE __forceinline
#    define CARB_PREFETCH(addr, write, level) _mm_prefetch(reinterpret_cast<char*>(addr), int(level))
enum class PrefetchLevel
{
    kHintNonTemporal = 0,
    kHintL1 = 1,
    kHintL2 = 2,
    kHintL3 = 3,
};
#elif CARB_COMPILER_GNUC
#    define CARB_ALWAYS_INLINE CARB_ATTRIBUTE(always_inline)
#    define CARB_PREFETCH(addr, write, level) __builtin_prefetch((addr), (write), int(level))
enum class PrefetchLevel
{
    kHintNonTemporal = 0,
    kHintL1 = 3,
    kHintL2 = 2,
    kHintL3 = 1,
};
#else
CARB_UNSUPPORTED_PLATFORM();
#endif

#define CARB_NOINLINE CARB_ATTRIBUTE(noinline) CARB_DECLSPEC(noinline)

#ifdef DOXYGEN_BUILD
#    define CARB_DEPRECATED(msg)
#    define CARB_FILE_DEPRECATED
#    define CARB_NOEXCEPT throw()
#    define DOXYGEN_EMPTY_CLASS                                                                                        \
        {                                                                                                              \
        }
#else
#    define CARB_DEPRECATED(msg) CARB_ATTRIBUTE(deprecated(msg)) CARB_DECLSPEC(deprecated(msg))
#    ifdef CARB_IGNORE_REMOVEFILE_WARNINGS
#        define CARB_FILE_DEPRECATED
#        define CARB_FILE_DEPRECATED_MSG(...)
#    else
#        define CARB_FILE_DEPRECATED_MSG(msg)                                                                          \
            CARB_PRAGMA(message("\x1b[33m" __FILE__ ":" CARB_STRINGIFY(                                                \
                __LINE__) ": " msg " (#define CARB_IGNORE_REMOVEFILE_WARNINGS to ignore these warnings)\x1b[0m"))      \
            CARB_PRAGMA(warning_see_message)
#        define CARB_FILE_DEPRECATED CARB_FILE_DEPRECATED_MSG("This file is no longer needed and will be removed soon")
#    endif
#    define CARB_NOEXCEPT noexcept
#    define DOXYGEN_EMPTY_CLASS
#endif

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T>
constexpr T align(T x, size_t alignment)
{
    return (T)(((size_t)x + alignment - 1) / alignment * alignment);
}
template <typename T>
T* align(T* x, size_t alignment)
{
    return (T*)(((size_t)x + alignment - 1) / alignment * alignment);
}
#endif
#define CARB_ALIGN(x, alignment) carb::align(x, alignment)

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T>
constexpr T alignedSize(const T& size, uint32_t alignment)
{
    return ((size + alignment - 1) / alignment) * alignment;
}
#endif
#define CARB_ALIGNED_SIZE(size, alignment) carb::alignedSize(size, alignment)

#define CARB_ALIGN_AS(T) alignas(T)

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T>
constexpr T divideCeil(T size, uint32_t divisor)
{
    static_assert(std::is_integral<T>::value, "Integral required.");
    return (size + divisor - 1) / divisor;
}
#endif
#define CARB_DIVIDE_CEIL(size, divisor) carb::divideCeil(size, divisor)

#if (CARB_HAS_CPP17 && defined(__cpp_lib_hardware_interference_size)) || defined(DOXYGEN_BUILD)
#    define CARB_CACHELINE_SIZE (std::hardware_destructive_interference_size)
#else
#    define CARB_CACHELINE_SIZE (64)
#endif

#define CARB_CACHELINE_ALIGN CARB_ALIGN_AS(CARB_CACHELINE_SIZE)

#if CARB_PLATFORM_WINDOWS
#    define CARB_ALLOCA(size) _alloca(size)
#elif CARB_PLATFORM_LINUX || CARB_PLATFORM_MACOS
#    define CARB_ALLOCA(size) alloca(size)
#else
CARB_UNSUPPORTED_PLATFORM();
#endif

#define CARB_STACK_ALLOC(T, number)                                                                                    \
    carb::align<T>(((number) ? (T*)CARB_ALLOCA((number) * sizeof(T) + alignof(T)) : nullptr), alignof(T))

#define CARB_MALLOC(size) std::malloc(size)

#define CARB_FREE(ptr) std::free(ptr)

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#    define __CARB_STRINGIFY(x) #    x
#endif
#define CARB_STRINGIFY(x) __CARB_STRINGIFY(x)

constexpr uint64_t kFnvBasis = 14695981039346656037ull;

constexpr uint64_t kFnvPrime = 1099511628211ull;

constexpr uint64_t fnv1aHash(const char* str, std::size_t n, uint64_t hash = kFnvBasis)
{
    return n > 0 ? fnv1aHash(str + 1, n - 1, (hash ^ *str) * kFnvPrime) : hash;
}

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <std::size_t N>
constexpr uint64_t fnv1aHash(const char (&array)[N])
{
    return fnv1aHash(&array[0], N - 1);
}
#endif

inline uint64_t hashString(const char* str, uint64_t hash = kFnvBasis)
{
    while (*str != '\0')
    {
        hash ^= static_cast<unsigned char>(*(str++));
        hash *= kFnvPrime;
    }
    return hash;
}

constexpr unsigned char tolower(unsigned char c)
{
    return detail::lowerTable[c];
};

constexpr unsigned char toupper(unsigned char c)
{
    return detail::upperTable[c];
}

inline uint64_t hashLowercaseString(const char* str, uint64_t hash = kFnvBasis)
{
    while (*str != '\0')
    {
        hash ^= tolower(static_cast<unsigned char>(*(str++)));
        hash *= kFnvPrime;
    }
    return hash;
}

inline uint64_t hashLowercaseBuffer(const void* buffer, size_t len, uint64_t hash = kFnvBasis)
{
    const unsigned char* data = static_cast<const unsigned char*>(buffer);
    const unsigned char* const end = data + len;
    while (data != end)
    {
        hash ^= tolower(*(data++));
        hash *= kFnvPrime;
    }
    return hash;
}

inline uint64_t hashUppercaseString(const char* str, uint64_t hash = kFnvBasis)
{
    while (*str != '\0')
    {
        hash ^= toupper(static_cast<unsigned char>(*(str++)));
        hash *= kFnvPrime;
    }
    return hash;
}

inline uint64_t hashUppercaseBuffer(const void* buffer, size_t len, uint64_t hash = kFnvBasis)
{
    const unsigned char* data = static_cast<const unsigned char*>(buffer);
    const unsigned char* const end = data + len;
    while (data != end)
    {
        hash ^= toupper(*(data++));
        hash *= kFnvPrime;
    }
    return hash;
}

inline uint64_t hashBuffer(const void* buffer, size_t length, uint64_t hash = kFnvBasis)
{
    const char* ptr = static_cast<const char*>(buffer);

    for (size_t i = 0; i < length; ++i)
    {
        hash ^= static_cast<unsigned char>(ptr[i]);
        hash *= kFnvPrime;
    }
    return hash;
}

template <class T>
constexpr uint64_t hashScalar(const T& type, uint64_t hash = kFnvBasis)
{
    static_assert(std::is_scalar<T>::value, "Unsupported type for hashing");
    return hashBuffer(reinterpret_cast<const char*>(std::addressof(type)), sizeof(type), hash);
}

inline constexpr uint64_t hashCombine(uint64_t hash1, uint64_t hash2) noexcept
{
    constexpr uint64_t kConstant{ 14313749767032793493ull };
    constexpr int kRotate = 47;

    hash2 *= kConstant;
    hash2 ^= (hash2 >> kRotate);
    hash2 *= kConstant;

    hash1 ^= hash2;
    hash1 *= kConstant;

    // Add an arbitrary value to prevent 0 hashing to 0
    hash1 += 0x42524143; // CARB
    return hash1;
}

// The string hash macro is guaranteed to evaluate at compile time. MSVC raises a warning for this, which we disable.
#if defined(__CUDACC__) || defined(DOXYGEN_BUILD)
#    define CARB_HASH_STRING(str) std::integral_constant<uint64_t, carb::fnv1aHash(str)>::value
#else
#    define CARB_HASH_STRING(str)                                                                                      \
        CARB_IGNOREWARNING_MSC_WITH_PUSH(4307) /* 'operator': integral constant overflow */                            \
        std::integral_constant<uint64_t, carb::fnv1aHash(str)>::value CARB_IGNOREWARNING_MSC_POP
#endif

#define CARB_HASH_TYPE(T) CARB_HASH_STRING(CARB_STRINGIFY(T))

// printf-like functions attributes
#if CARB_COMPILER_GNUC || defined(DOXYGEN_BUILD)
#    define CARB_PRINTF_FUNCTION(fmt_ordinal, args_ordinal) CARB_ATTRIBUTE(format(printf, fmt_ordinal, args_ordinal))
#elif CARB_COMPILER_MSC
// Microsoft suggest to use SAL annotations _Printf_format_string_ and _Printf_format_string_params_ for
// printf-like functions. Unfortunately it does not work properly for custom printf-like function pointers.
// So, instead of defining marker attribute for format string, we use the "fake printf" trick to force compiler
// checks and keep function attribute empty.
#    define CARB_PRINTF_FUNCTION(fmt_ordinal, args_ordinal)
#else
#    define CARB_PRINTF_FUNCTION(fmt_ordinal, args_ordinal)
#endif

struct ValueInitFirst
{
    constexpr explicit ValueInitFirst() = default;
};

struct InitBoth
{
    constexpr explicit InitBoth() = default;
};

template <class First, class Second, bool = std::is_empty<First>::value && !std::is_final<First>::value>
class EmptyMemberPair : private First
{
public:
    using FirstType = First;
    using SecondType = Second;

    template <class... Args2>
    constexpr explicit EmptyMemberPair(ValueInitFirst, Args2&&... args)
        : First{}, second{ std::forward<Args2>(args)... }
    {
    }

    template <class Arg1, class... Args2>
    constexpr explicit EmptyMemberPair(InitBoth, Arg1&& arg1, Args2&&... args2)
        : First(std::forward<Arg1>(arg1)), second(std::forward<Args2>(args2)...)
    {
    }

    constexpr FirstType& first() noexcept
    {
        return *this;
    }

    constexpr const FirstType& first() const noexcept
    {
        return *this;
    }

    SecondType second;
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <class First, class Second>
class EmptyMemberPair<First, Second, false>
{
public:
    using FirstType = First;
    using SecondType = Second;

    template <class... Args2>
    constexpr explicit EmptyMemberPair(ValueInitFirst, Args2&&... args)
        : m_first(), second(std::forward<Args2>(args)...)
    {
    }

    template <class Arg1, class... Args2>
    constexpr explicit EmptyMemberPair(InitBoth, Arg1&& arg1, Args2&&... args2)
        : m_first(std::forward<Arg1>(arg1)), second(std::forward<Args2>(args2)...)
    {
    }

    constexpr FirstType& first() noexcept
    {
        return m_first;
    }
    constexpr const FirstType& first() const noexcept
    {
        return m_first;
    }

private:
    FirstType m_first;

public:
    SecondType second;
};
#endif

} // namespace carb

template <class T>
CARB_NODISCARD constexpr const T& carb_min(const T& left, const T& right) noexcept(noexcept(left < right))
{
    return left < right ? left : right;
}

template <class T>
CARB_NODISCARD constexpr const T& carb_max(const T& left, const T& right) noexcept(noexcept(left < right))
{
    return left < right ? right : left;
}

#if CARB_POSIX || defined(DOXYGEN_BUILD)
#    define CARB_RETRY_EINTR(op)                                                                                       \
        [&] {                                                                                                          \
            decltype(op) ret_;                                                                                         \
            while ((ret_ = (op)) < 0 && errno == EINTR)                                                                \
            {                                                                                                          \
            }                                                                                                          \
            return ret_;                                                                                               \
        }()
#else
#    define CARB_RETRY_EINTR(op) (op)
#endif

template <class... Args>
void CARB_UNUSED(Args&&... CARB_DOC_ONLY(args))
{
}

#define CARB_UNIMPLEMENTED(msg, ...)                                                                                   \
    do                                                                                                                 \
    {                                                                                                                  \
        CARB_FATAL_UNLESS(false, (msg), ##__VA_ARGS__);                                                                \
        std::terminate();                                                                                              \
    } while (0)

#define CARB_MACOS_UNIMPLEMENTED() CARB_UNIMPLEMENTED("Unimplemented on Mac OS")

#if defined(CARB_INCLUDE_PURIFY_NAME) && !defined(DOXYGEN_BUILD)
#    ifdef __COUNTER__
#        define CARB_INCLUDE_PURIFY_TEST(...)                                                                          \
            inline void CARB_JOIN(CARB_INCLUDE_PURIFY_NAME, __COUNTER__)()                                             \
                __VA_ARGS__ static_assert(true, "Semicolon required")
#    else
#        define CARB_INCLUDE_PURIFY_TEST(...)                                                                          \
            inline void CARB_JOIN(CARB_INCLUDE_PURIFY_NAME, __LINE__)()                                                \
                __VA_ARGS__ static_assert(true, "Semicolon required")
#    endif
#else
#    define CARB_INCLUDE_PURIFY_TEST(...)
#endif

// Undefine locals
#undef CARBLOCAL_HAS_NODISCARD
#undef CARBLOCAL_HAS_NODISCARD_MSG
#undef CARBLOCAL_HAS_FALLTHROUGH
#undef CARBLOCAL_HAS_MAYBE_UNUSED
#undef CARBLOCAL_HAS_LIKELY
#undef CARBLOCAL_HAS_UNLIKELY
#undef CARBLOCAL_HAS_NO_UNIQUE_ADDRESS