// Copyright (c) 2019-2022, 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 <cstdint>
#include <cstdlib>
#include <cstddef>
#include <carb/flatcache/Defines.h>

#if USING( WINDOWS_BUILD )
#include <intrin.h>
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
// no linux-specific includes at this time
#else // #if USING( WINDOWS_BUILD )
#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )

namespace carb
{
namespace flatcache
{
inline uint32_t clz32( const uint32_t x )
{
#if USING( WINDOWS_BUILD )
	DWORD z;
	return _BitScanReverse( &z, x ) ? 31 - z : 32;
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
	return  x ? __builtin_clz( x ) : 32;
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}


inline uint32_t clz64( const uint64_t x )
{
#if USING( WINDOWS_BUILD )
	DWORD z;
	return _BitScanReverse64( &z, x ) ? 63 - z : 64;
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
	return  x ? __builtin_clzll( x ) : 64;
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}


inline uint32_t ctz32( const uint32_t x )
{
#if USING( WINDOWS_BUILD )
	DWORD z;
	return _BitScanForward( &z, x ) ? z : 32;
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
	return  x ? __builtin_ctz( x ) : 32;
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}


inline uint32_t ctz64( const uint64_t x )
{
#if USING( WINDOWS_BUILD )
	DWORD z;
	return _BitScanForward64( &z, x ) ? z : 64;
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
	return  x ? __builtin_ctzll( x ) : 64;
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}

inline uint64_t bswap64( const uint64_t x )
{
#if USING( WINDOWS_BUILD )
	return _byteswap_uint64( x );
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
	return  __builtin_bswap64 ( x );
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}

inline uint64_t rotr64( const uint64_t value, const int shift )
{
#if USING( WINDOWS_BUILD )
	return _rotr64( value, shift );
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
    return (value >> shift) | (value << (64 - shift));
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}

inline uint64_t rotl64( const uint64_t value, const int shift )
{
#if USING( WINDOWS_BUILD )
	return _rotl64( value, shift );
#elif USING( LINUX_BUILD ) // #if USING( WINDOWS_BUILD )
    return (value << shift) | (value >> (64 - shift));
#else // #if USING( WINDOWS_BUILD )
	#error "Unsupported platform"
#endif // #if USING( WINDOWS_BUILD )
}

} // namespace flatcache
} // namespace carb