//
// ByteOrder.h
//
// Library: Engine
// Package: Core
// Module: ByteOrder
//
//


#ifndef WISHBONE_BYTEORDER_H_
#define WISHBONE_BYTEORDER_H_


#include "Wishbone/Foundation.h"
#include "Wishbone/Types.h"
#if defined(_MSC_VER)
#include <stdlib.h> // builtins
#endif


namespace Wishbone
{

/// This class contains a number of static methods to convert between big-endian and little-endian integers of various sizes.
class FOUNDATION_API ByteOrder
{
public:
static Int16 SwapInt16(Int16 value);
static Wishbone::UInt16 SwapUInt16(Wishbone::UInt16 value);
static Int32 SwapInt32(Int32 value);
static Wishbone::UInt32 SwapUInt32(Wishbone::UInt32 value);
#if defined(WISHBONE_HAVE_INT64)
static Int64 SwapInt64(Int64 value);
static Wishbone::UInt64 SwapUInt64(Wishbone::UInt64 value);
#endif
};


#if !defined(WISHBONE_NO_BYTESWAP_BUILTINS)
#if defined(_MSC_VER)
#if (WISHBONE_MSVC_VERSION > 71)
#define WISHBONE_HAVE_MSC_BYTESWAP 1
#endif
#elif defined(__clang__)
#if __has_builtin(__builtin_bswap32)
#define WISHBONE_HAVE_GCC_BYTESWAP 1
#endif
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
#define WISHBONE_HAVE_GCC_BYTESWAP 1
#endif
#endif


//
// inlines
//
inline Wishbone::UInt16 ByteOrder::SwapUInt16(Wishbone::UInt16 value)
{
#if defined(WISHBONE_HAVE_MSC_BYTESWAP)
return _byteswap_ushort(value);
#elif defined(__i386__) && defined(__GNUC__)
__asm__("xchgb %b0, %h0" : "+q" (value));
return value;
#elif defined(__ppc__) && defined(__GNUC__)
Wishbone::UInt16 result;
__asm__("lhbrx %0,0,%1" : "=r" (result) : "r" (&value), "m" (value));
return result;
#else
// swap bytes
Wishbone::UInt16 result;
result = ((value << 8) & 0xFF00) | ((value >> 8) & 0x00FF);
return result;
#endif
}


inline Int16 ByteOrder::SwapInt16(Int16 value)
{
return Int16(SwapUInt16(Wishbone::UInt16(value)));
}


inline Wishbone::UInt32 ByteOrder::SwapUInt32(Wishbone::UInt32 value)
{
#if defined(WISHBONE_HAVE_MSC_BYTESWAP)
return _byteswap_ulong(value);
#elif defined(WISHBONE_HAVE_GCC_BYTESWAP)
return __builtin_bswap32(value);
#elif defined(__i386__) && defined(__GNUC__)
__asm__("bswap %0" : "+r" (value));
return value;
#elif defined(__ppc__) && defined(__GNUC__)
Wishbone::UInt32 result;
__asm__("lwbrx %0,0,%1" : "=r" (result) : "r" (&value), "m" (value));
return result;
#else
// swap words then bytes
Wishbone::UInt32 result;
result = ((value & 0x000000FF) << 24) | ((value & 0x0000FF00) << 8) | ((value >> 8) & 0x0000FF00) | ((value >> 24) & 0x000000FF);
return result;
#endif
}


inline Int32 ByteOrder::SwapInt32(Int32 value)
{
return Int32(SwapUInt32(Wishbone::UInt32(value)));
}


#if defined(WISHBONE_HAVE_INT64)

inline Wishbone::UInt64 ByteOrder::SwapUInt64(Wishbone::UInt64 value)
{
#if defined(WISHBONE_HAVE_MSC_BYTESWAP)
return _byteswap_uint64(value);
#elif defined(WISHBONE_HAVE_GCC_BYTESWAP)
return __builtin_bswap64(value);
#else
union Swap {
Wishbone::UInt64 sv;
Wishbone::UInt32 ul[2];
} tmp, result;
tmp.sv = value;
result.ul[0] = SwapInt32(tmp.ul[1]);
result.ul[1] = SwapInt32(tmp.ul[0]);
return result.sv;
#endif
}


inline Int64 ByteOrder::SwapInt64(Int64 value)
{
return Int64(SwapUInt64(Wishbone::UInt64(value)));
}
#endif // WISHBONE_HAVE_INT64


} // namespace Wishbone


#endif // WISHBONE_BYTEORDER_H_