//
// Copyright 2010-2011 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#pragma once

/***********************************************************************
 * Platform-specific implementation details for byteswap below:
 **********************************************************************/
#if defined(UHD_MSVC)
#    include <cstdlib>

UHD_FORCE_INLINE uint16_t uhd::byteswap(uint16_t x)
{
    return _byteswap_ushort(x);
}

UHD_FORCE_INLINE uint32_t uhd::byteswap(uint32_t x)
{
    return _byteswap_ulong(x);
}

UHD_FORCE_INLINE uint64_t uhd::byteswap(uint64_t x)
{
    return _byteswap_uint64(x);
}

#elif defined(UHD_PLATFORM_MACOS)
#    include <libkern/OSByteOrder.h>

UHD_FORCE_INLINE uint16_t uhd::byteswap(uint16_t x)
{
    return OSSwapInt16(x);
}

UHD_FORCE_INLINE uint32_t uhd::byteswap(uint32_t x)
{
    return OSSwapInt32(x);
}

UHD_FORCE_INLINE uint64_t uhd::byteswap(uint64_t x)
{
    return OSSwapInt64(x);
}

#elif defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3

UHD_FORCE_INLINE uint16_t uhd::byteswap(uint16_t x)
{
    return (x >> 8) | (x << 8); // DNE return __builtin_bswap16(x);
}

UHD_FORCE_INLINE uint32_t uhd::byteswap(uint32_t x)
{
    return __builtin_bswap32(x);
}

UHD_FORCE_INLINE uint64_t uhd::byteswap(uint64_t x)
{
    return __builtin_bswap64(x);
}

#elif defined(UHD_PLATFORM_LINUX)
#    include <byteswap.h>

UHD_FORCE_INLINE uint16_t uhd::byteswap(uint16_t x)
{
    return bswap_16(x);
}

UHD_FORCE_INLINE uint32_t uhd::byteswap(uint32_t x)
{
    return bswap_32(x);
}

UHD_FORCE_INLINE uint64_t uhd::byteswap(uint64_t x)
{
    return bswap_64(x);
}

#else // http://www.koders.com/c/fidB93B34CD44F0ECF724F1A4EAE3854BA2FE692F59.aspx

UHD_FORCE_INLINE uint16_t uhd::byteswap(uint16_t x)
{
    return (x >> 8) | (x << 8);
}

UHD_FORCE_INLINE uint32_t uhd::byteswap(uint32_t x)
{
    return (uint32_t(uhd::byteswap(uint16_t(x & 0xfffful))) << 16)
           | (uhd::byteswap(uint16_t(x >> 16)));
}

UHD_FORCE_INLINE uint64_t uhd::byteswap(uint64_t x)
{
    return (uint64_t(uhd::byteswap(uint32_t(x & 0xffffffffull))) << 32)
           | (uhd::byteswap(uint32_t(x >> 32)));
}

#endif

/***********************************************************************
 * Define the templated network to/from host conversions
 **********************************************************************/
namespace uhd {

template <typename T>
UHD_FORCE_INLINE T ntohx(T num)
{
#ifdef UHD_BIG_ENDIAN
    return num;
#else
    return uhd::byteswap(num);
#endif
}

template <typename T>
UHD_FORCE_INLINE T htonx(T num)
{
#ifdef UHD_BIG_ENDIAN
    return num;
#else
    return uhd::byteswap(num);
#endif
}

template <typename T>
UHD_FORCE_INLINE T wtohx(T num)
{
#ifdef UHD_BIG_ENDIAN
    return uhd::byteswap(num);
#else
    return num;
#endif
}

template <typename T>
UHD_FORCE_INLINE T htowx(T num)
{
#ifdef UHD_BIG_ENDIAN
    return uhd::byteswap(num);
#else
    return num;
#endif
}

template <endianness_t endianness, typename T>
UHD_FORCE_INLINE T htolx(T num)
{
#ifdef UHD_BIG_ENDIAN
    if constexpr (endianness == ENDIANNESS_BIG) {
        return num;
    } else {
        return uhd::byteswap(num);
    }
#else // if UHD_LITTLE_ENDIAN
    if constexpr (endianness == ENDIANNESS_LITTLE) {
        return num;
    } else {
        return uhd::byteswap(num);
    }
#endif
}

template <endianness_t endianness, typename T>
UHD_FORCE_INLINE T ltohx(T num)
{
#ifdef UHD_BIG_ENDIAN
    if constexpr (endianness == ENDIANNESS_BIG) {
        return num;
    } else {
        return uhd::byteswap(num);
    }
#else // if UHD_LITTLE_ENDIAN
    if constexpr (endianness == ENDIANNESS_LITTLE) {
        return num;
    } else {
        return uhd::byteswap(num);
    }
#endif
}

} /* namespace uhd */
