/*========================== begin_copyright_notice ============================
Copyright (C) 2021 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

/*!
 * @file Basic utilities
 */

#ifndef GT_BASIC_UTILS_H_
#define GT_BASIC_UTILS_H_

#include "gt_basic_defs.h"

namespace gtpin
{
#pragma pack(push, 8)

/* ============================================================================================= */
// Bit scan utilities
/* ============================================================================================= */
/*!
 * @return Position of the most significant bit set 1 in the specified integer value.
 *         If val == 0, the return value is zero
 */
inline uint32_t Bsr32(uint32_t val)
{
    // Binary search
    uint32_t pos = 0;
    { uint32_t hi = (val >> 16); if (hi) { val = hi; pos += 16; } }
    { uint32_t hi = (val >> 8);  if (hi) { val = hi; pos += 8;  } }
    { uint32_t hi = (val >> 4);  if (hi) { val = hi; pos += 4;  } }
    { uint32_t hi = (val >> 2);  if (hi) { val = hi; pos += 2;  } }
    { uint32_t hi = (val >> 1);  if (hi) {         ; pos += 1;  } }
    return pos;
}

inline uint32_t Bsr64(uint64_t val)
{
    uint32_t hi = uint32_t(val >> 32);
    return (hi ? (Bsr32(hi) + 32) : Bsr32(uint32_t(val)));
}

/*!
 * @return Position of the least significant bit set 1 in the specified integer value.
 *         If val == 0, the return value is sizeof(val)*8 -1
 */
inline uint32_t Bsf32(uint32_t val)
{
    // Binary search
    uint32_t pos = 0;
    if ((val & 0xffff) == 0) { val >>= 16; pos += 16; }
    if ((val & 0xff)   == 0) { val >>= 8;  pos += 8; }
    if ((val & 0xf)    == 0) { val >>= 4;  pos += 4; }
    if ((val & 0x3)    == 0) { val >>= 2;  pos += 2; }
    if ((val & 0x1)    == 0) {             pos += 1; }
    return pos;
}

inline uint32_t Bsf64(uint64_t val)
{
    return (uint32_t(val) ? Bsf32(uint32_t(val)) : (Bsf32(uint32_t(val >> 32)) + 32));
}

/// @return Number of bits set in 'value'
template <typename T> uint32_t BitCount(T value)
{
    uint32_t count; for (count = 0; value != 0 ; count++) { value &= (value - 1); }
    return count;
}

/* ============================================================================================= */
// Rounding (alignment) utilities
/* ============================================================================================= */

/// @return TRUE, if 'value' is power of two
template <typename T> constexpr bool IsPower2(T value) { return ((value & (value - 1)) == 0); }

/// @return TRUE, if 'value' is a multiple of given alignment, which is a power of two
template <typename T> constexpr bool IsAligned(T value, size_t alignment)
{
    return ((value & (alignment - 1)) == 0);
}

/// Specialization of the IsAligned function for pointer type
template <typename T> constexpr bool IsAligned(T * ptr, size_t alignment)
{
    return IsAligned(reinterpret_cast<uintptr_t>(ptr), alignment);
}

/// Round integer of type <T> up to given alignment, which is a power of two.
template <typename T> constexpr  T AlignUp(T value, size_t alignment)
{
    return ((value + static_cast<T>(alignment - 1)) & ~static_cast<T>(alignment - 1));
}
/// Specialization of the AlignUp function for pointer type
template <typename T> constexpr T * AlignUp(T * ptr, size_t alignment)
{
    return reinterpret_cast<T *>(AlignUp(reinterpret_cast<uintptr_t>(ptr), alignment));
}

/// Round integer of type <T> down to given alignment, which is a power of two.
template <typename T> constexpr T AlignDown(T value, size_t alignment)
{
    return (value & ~static_cast<T>(alignment - 1));
}
/// Specialization of the AlignDown function for pointer type
template <typename T> constexpr T * AlignDown(T * ptr, size_t alignment)
{
    return reinterpret_cast<T *>(AlignDown(reinterpret_cast<uintptr_t>(ptr), alignment));
}

/// Round integer of type <T> up to nearest power of two
template <typename T> T AlignPower2Up(T value);

/*!
 * Specialization of the AlignPower2Up() template for 32-bit integer type
 * @note if value is greater than 2**31, the function returns zero.
 */
template <> inline uint32_t AlignPower2Up(uint32_t value)
{
    return ((value <= 2) ? value : uint32_t(2) << Bsr32(value-1));
}

/*!
 * Specialization of the AlignPower2Up() template for 64-bit integer type
 * @note if value is greater than 2**63, the function returns zero.
 */
template <> inline uint64_t AlignPower2Up(uint64_t value)
{
    return ((value <= 2) ? value : uint64_t(2) << Bsr64(value-1));
}

/// Round integer of type <T> down to nearest power of two
template <typename T> T AlignPower2Down(T value);

/// Specialization of the AlignPower2Down() template for 32-bit integer type
template <> inline uint32_t AlignPower2Down(uint32_t value)
{
    return ((value == 0) ? 0 : uint32_t(1) << Bsr32(value));
}

/// Specialization of the AlignPower2Down() template for 64-bit integer type
template <> inline uint64_t AlignPower2Down(uint64_t value)
{
    return ((value == 0) ? 0 : uint64_t(1) << Bsr64(value));
}

/// Round integer of type <T> up to given granularity, which is not necessarily a power of two.
template <typename T> inline T RoundUp(T value, size_t granularity)
{
    const T v = value + static_cast<T>(granularity - 1);
    return v - (v % granularity);
}
/// Specialization of the RoundUp function for pointer type
template <typename T> inline T * RoundUp(T * ptr, size_t granularity)
{
    return reinterpret_cast<T *>(RoundUp(reinterpret_cast<uintptr_t>(ptr), granularity));
}

/// Round integer of type <T> down to given alignment,  which is not necessarily a power of two.
template <typename T> constexpr T RoundDown(T value, size_t granularity)
{
    return value - (value % granularity);
}
/// Specialization of the RoundDown function for pointer type
template <typename T> constexpr T * RoundDown(T * ptr, size_t granularity)
{
    return reinterpret_cast<T *>(RoundDown(reinterpret_cast<uintptr_t>(ptr), granularity));
}

/* ============================================================================================= */
// Bit field accessors and modifiers
/* ============================================================================================= */

/// @return value of integral type T in which bits [firstPos, lastPos] are set one
template <typename T = uint64_t> constexpr T BitMask(uint32_t firstPos, uint32_t lastPos)
{
    return BitField<T>(firstPos, lastPos - firstPos + 1).Mask();
}

/// @return value stored in the [firstPos, lastPos] bits of parameter 'val'
template <typename T = uint64_t> constexpr T ExtractBits(T val, uint32_t firstPos, uint32_t lastPos)
{
    return BitField<T>(firstPos, lastPos - firstPos + 1).GetValue(val);
}

/// Set value 'bitVal' in the [firstPos, lastPos] bits of parameter 'val', and return modified 'val'
template <typename T = uint64_t> inline T ReplaceBits(T val, T bitVal, uint32_t firstPos, uint32_t lastPos)
{
    return BitField<T>(firstPos, lastPos - firstPos + 1).SetValue(val, bitVal);
}

/* ============================================================================================= */
// Sign-extension utilities
/* ============================================================================================= */

/// Sign-extend the width-bit value 'val' of a signed integer type 'T'
template <uint32_t width, typename T = int32_t> inline T SignExtend(T val)
{
    GTPIN_STATIC_ASSERT(width <= (sizeof(T) * 8));
    GTPIN_STATIC_ASSERT(T(-1) < T(0));
    constexpr uint32_t shift = (sizeof(T) * 8) - width;
    return (val << shift) >> shift;
}

/// Sign-extend the width-bit value 'val' of a signed integer type 'T'
template <typename T = int32_t> inline T SignExtend(T val, uint32_t width)
{
    GTPIN_STATIC_ASSERT(T(-1) < T(0));
    const uint32_t shift = (sizeof(T) * 8) - width;
    return (val << shift) >> shift;
}

/// @return true, if signed integer 'val' can be represented in a width-bit field
template <typename T = int32_t> inline bool RepresentableSignedInt(T val, uint32_t width)
{
    return (val == SignExtend(val, width));
}

#pragma pack(pop)
}
#endif
