/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/Assertions.h"
#include "mozilla/EndianUtils.h"

#include <bit>
#include <stddef.h>

using mozilla::BigEndian;
using mozilla::LittleEndian;
using mozilla::NativeEndian;

template <typename T>
void TestSingleSwap(T aValue, T aSwappedValue) {
  if constexpr (std::endian::native == std::endian::little) {
    MOZ_RELEASE_ASSERT(NativeEndian::swapToBigEndian(aValue) == aSwappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapFromBigEndian(aValue) ==
                       aSwappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapToNetworkOrder(aValue) ==
                       aSwappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapFromNetworkOrder(aValue) ==
                       aSwappedValue);
  } else {
    MOZ_RELEASE_ASSERT(NativeEndian::swapToLittleEndian(aValue) ==
                       aSwappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapFromLittleEndian(aValue) ==
                       aSwappedValue);
  }
}

template <typename T>
void TestSingleNoSwap(T aValue, T aUnswappedValue) {
  if constexpr (std::endian::native == std::endian::little) {
    MOZ_RELEASE_ASSERT(NativeEndian::swapToLittleEndian(aValue) ==
                       aUnswappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapFromLittleEndian(aValue) ==
                       aUnswappedValue);
  } else {
    MOZ_RELEASE_ASSERT(NativeEndian::swapToBigEndian(aValue) ==
                       aUnswappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapFromBigEndian(aValue) ==
                       aUnswappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapToNetworkOrder(aValue) ==
                       aUnswappedValue);
    MOZ_RELEASE_ASSERT(NativeEndian::swapFromNetworkOrder(aValue) ==
                       aUnswappedValue);
  }
}

// EndianUtils.h functions are declared as protected in a base class and
// then re-exported as public in public derived classes.  The
// standardese around explicit instantiation of templates is not clear
// in such cases.  Provide these wrappers to make things more explicit.
// For your own enlightenment, you may wish to peruse:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56152 and subsequently
// http://j.mp/XosS6S .
#define WRAP_COPYTO(NAME)                               \
  template <typename T>                                 \
  void NAME(void* aDst, const T* aSrc, size_t aCount) { \
    NativeEndian::NAME<T>(aDst, aSrc, aCount);          \
  }

WRAP_COPYTO(copyAndSwapToLittleEndian)
WRAP_COPYTO(copyAndSwapToBigEndian)
WRAP_COPYTO(copyAndSwapToNetworkOrder)

#define WRAP_COPYFROM(NAME)                             \
  template <typename T>                                 \
  void NAME(T* aDst, const void* aSrc, size_t aCount) { \
    NativeEndian::NAME<T>(aDst, aSrc, aCount);          \
  }

WRAP_COPYFROM(copyAndSwapFromLittleEndian)
WRAP_COPYFROM(copyAndSwapFromBigEndian)
WRAP_COPYFROM(copyAndSwapFromNetworkOrder)

#define WRAP_IN_PLACE(NAME)            \
  template <typename T>                \
  void NAME(T* aP, size_t aCount) {    \
    NativeEndian::NAME<T>(aP, aCount); \
  }
WRAP_IN_PLACE(swapToLittleEndianInPlace)
WRAP_IN_PLACE(swapFromLittleEndianInPlace)
WRAP_IN_PLACE(swapToBigEndianInPlace)
WRAP_IN_PLACE(swapFromBigEndianInPlace)
WRAP_IN_PLACE(swapToNetworkOrderInPlace)
WRAP_IN_PLACE(swapFromNetworkOrderInPlace)

enum SwapExpectation { Swap, NoSwap };

template <typename T, size_t Count>
void TestBulkSwapToSub(enum SwapExpectation aExpectSwap,
                       const T (&aValues)[Count],
                       void (*aSwapperFunc)(void*, const T*, size_t),
                       T (*aReaderFunc)(const void*)) {
  const size_t arraySize = 2 * Count;
  const size_t bufferSize = arraySize * sizeof(T);
  static uint8_t buffer[bufferSize];
  const uint8_t fillValue = 0xa5;
  static uint8_t checkBuffer[bufferSize];

  MOZ_RELEASE_ASSERT(bufferSize > 2 * sizeof(T));

  memset(checkBuffer, fillValue, bufferSize);

  for (size_t startPosition = 0; startPosition < sizeof(T); ++startPosition) {
    for (size_t nValues = 0; nValues < Count; ++nValues) {
      memset(buffer, fillValue, bufferSize);
      aSwapperFunc(buffer + startPosition, aValues, nValues);

      MOZ_RELEASE_ASSERT(memcmp(buffer, checkBuffer, startPosition) == 0);
      size_t valuesEndPosition = startPosition + sizeof(T) * nValues;
      MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition,
                                checkBuffer + valuesEndPosition,
                                bufferSize - valuesEndPosition) == 0);
      if (aExpectSwap == NoSwap) {
        MOZ_RELEASE_ASSERT(
            memcmp(buffer + startPosition, aValues, nValues * sizeof(T)) == 0);
      }
      for (size_t i = 0; i < nValues; ++i) {
        MOZ_RELEASE_ASSERT(
            aReaderFunc(buffer + startPosition + sizeof(T) * i) == aValues[i]);
      }
    }
  }
}

template <typename T, size_t Count>
void TestBulkSwapFromSub(enum SwapExpectation aExpectSwap,
                         const T (&aValues)[Count],
                         void (*aSwapperFunc)(T*, const void*, size_t),
                         T (*aReaderFunc)(const void*)) {
  const size_t arraySize = 2 * Count;
  const size_t bufferSize = arraySize * sizeof(T);
  static T buffer[arraySize];
  const uint8_t fillValue = 0xa5;
  static T checkBuffer[arraySize];

  memset(checkBuffer, fillValue, bufferSize);

  for (size_t startPosition = 0; startPosition < Count; ++startPosition) {
    for (size_t nValues = 0; nValues < (Count - startPosition); ++nValues) {
      memset(buffer, fillValue, bufferSize);
      aSwapperFunc(buffer + startPosition, aValues, nValues);

      MOZ_RELEASE_ASSERT(
          memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0);
      size_t valuesEndPosition = startPosition + nValues;
      MOZ_RELEASE_ASSERT(
          memcmp(buffer + valuesEndPosition, checkBuffer + valuesEndPosition,
                 (arraySize - valuesEndPosition) * sizeof(T)) == 0);
      if (aExpectSwap == NoSwap) {
        MOZ_RELEASE_ASSERT(
            memcmp(buffer + startPosition, aValues, nValues * sizeof(T)) == 0);
      }
      for (size_t i = 0; i < nValues; ++i) {
        MOZ_RELEASE_ASSERT(aReaderFunc(buffer + startPosition + i) ==
                           aValues[i]);
      }
    }
  }
}

template <typename T, size_t Count>
void TestBulkInPlaceSub(enum SwapExpectation aExpectSwap,
                        const T (&aValues)[Count],
                        void (*aSwapperFunc)(T*, size_t),
                        T (*aReaderFunc)(const void*)) {
  const size_t bufferCount = 4 * Count;
  const size_t bufferSize = bufferCount * sizeof(T);
  static T buffer[bufferCount];
  const T fillValue = 0xa5;
  static T checkBuffer[bufferCount];

  MOZ_RELEASE_ASSERT(bufferSize > 2 * sizeof(T));

  memset(checkBuffer, fillValue, bufferSize);

  for (size_t startPosition = 0; startPosition < Count; ++startPosition) {
    for (size_t nValues = 0; nValues < Count; ++nValues) {
      memset(buffer, fillValue, bufferSize);
      memcpy(buffer + startPosition, aValues, nValues * sizeof(T));
      aSwapperFunc(buffer + startPosition, nValues);

      MOZ_RELEASE_ASSERT(
          memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0);
      size_t valuesEndPosition = startPosition + nValues;
      MOZ_RELEASE_ASSERT(
          memcmp(buffer + valuesEndPosition, checkBuffer + valuesEndPosition,
                 bufferSize - valuesEndPosition * sizeof(T)) == 0);
      if (aExpectSwap == NoSwap) {
        MOZ_RELEASE_ASSERT(
            memcmp(buffer + startPosition, aValues, nValues * sizeof(T)) == 0);
      }
      for (size_t i = 0; i < nValues; ++i) {
        MOZ_RELEASE_ASSERT(aReaderFunc(buffer + startPosition + i) ==
                           aValues[i]);
      }
    }
  }
}

template <typename T>
struct Reader {};

#define SPECIALIZE_READER(TYPE, READ_FUNC)                                     \
  template <>                                                                  \
  struct Reader<TYPE> {                                                        \
    static TYPE readLE(const void* aP) { return LittleEndian::READ_FUNC(aP); } \
    static TYPE readBE(const void* aP) { return BigEndian::READ_FUNC(aP); }    \
  };

SPECIALIZE_READER(uint16_t, readUint16)
SPECIALIZE_READER(uint32_t, readUint32)
SPECIALIZE_READER(uint64_t, readUint64)
SPECIALIZE_READER(int16_t, readInt16)
SPECIALIZE_READER(int32_t, readInt32)
SPECIALIZE_READER(int64_t, readInt64)

template <typename T, size_t Count>
void TestBulkSwap(const T (&aBytes)[Count]) {
  if constexpr (std::endian::native == std::endian::little) {
    TestBulkSwapToSub(Swap, aBytes, copyAndSwapToBigEndian<T>,
                      Reader<T>::readBE);
    TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromBigEndian<T>,
                        Reader<T>::readBE);
    TestBulkSwapToSub(Swap, aBytes, copyAndSwapToNetworkOrder<T>,
                      Reader<T>::readBE);
    TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromNetworkOrder<T>,
                        Reader<T>::readBE);
  } else {
    TestBulkSwapToSub(Swap, aBytes, copyAndSwapToLittleEndian<T>,
                      Reader<T>::readLE);
    TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromLittleEndian<T>,
                        Reader<T>::readLE);
  }
}

template <typename T, size_t Count>
void TestBulkNoSwap(const T (&aBytes)[Count]) {
  if constexpr (std::endian::native == std::endian::little) {
    TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToLittleEndian<T>,
                      Reader<T>::readLE);
    TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromLittleEndian<T>,
                        Reader<T>::readLE);
  } else {
    TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToBigEndian<T>,
                      Reader<T>::readBE);
    TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromBigEndian<T>,
                        Reader<T>::readBE);
    TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToNetworkOrder<T>,
                      Reader<T>::readBE);
    TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromNetworkOrder<T>,
                        Reader<T>::readBE);
  }
}

template <typename T, size_t Count>
void TestBulkInPlaceSwap(const T (&aBytes)[Count]) {
  if constexpr (std::endian::native == std::endian::little) {
    TestBulkInPlaceSub(Swap, aBytes, swapToBigEndianInPlace<T>,
                       Reader<T>::readBE);
    TestBulkInPlaceSub(Swap, aBytes, swapFromBigEndianInPlace<T>,
                       Reader<T>::readBE);
    TestBulkInPlaceSub(Swap, aBytes, swapToNetworkOrderInPlace<T>,
                       Reader<T>::readBE);
    TestBulkInPlaceSub(Swap, aBytes, swapFromNetworkOrderInPlace<T>,
                       Reader<T>::readBE);
  } else {
    TestBulkInPlaceSub(Swap, aBytes, swapToLittleEndianInPlace<T>,
                       Reader<T>::readLE);
    TestBulkInPlaceSub(Swap, aBytes, swapFromLittleEndianInPlace<T>,
                       Reader<T>::readLE);
  }
}

template <typename T, size_t Count>
void TestBulkInPlaceNoSwap(const T (&aBytes)[Count]) {
  if constexpr (std::endian::native == std::endian::little) {
    TestBulkInPlaceSub(NoSwap, aBytes, swapToLittleEndianInPlace<T>,
                       Reader<T>::readLE);
    TestBulkInPlaceSub(NoSwap, aBytes, swapFromLittleEndianInPlace<T>,
                       Reader<T>::readLE);
  } else {
    TestBulkInPlaceSub(NoSwap, aBytes, swapToBigEndianInPlace<T>,
                       Reader<T>::readBE);
    TestBulkInPlaceSub(NoSwap, aBytes, swapFromBigEndianInPlace<T>,
                       Reader<T>::readBE);
    TestBulkInPlaceSub(NoSwap, aBytes, swapToNetworkOrderInPlace<T>,
                       Reader<T>::readBE);
    TestBulkInPlaceSub(NoSwap, aBytes, swapFromNetworkOrderInPlace<T>,
                       Reader<T>::readBE);
  }
}

int main() {
  static const uint8_t unsigned_bytes[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                                             0x07, 0x08, 0x01, 0x02, 0x03, 0x04,
                                             0x05, 0x06, 0x07, 0x08};
  static const int8_t signed_bytes[16] = {
      -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08,
      -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08};
  static const uint16_t uint16_values[8] = {0x0102, 0x0304, 0x0506, 0x0708,
                                            0x0102, 0x0304, 0x0506, 0x0708};
  static const int16_t int16_values[8] = {
      int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8),
      int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8)};
  static const uint32_t uint32_values[4] = {0x01020304, 0x05060708, 0x01020304,
                                            0x05060708};
  static const int32_t int32_values[4] = {
      int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8), int32_t(0xf1f2f3f4),
      int32_t(0xf5f6f7f8)};
  static const uint64_t uint64_values[2] = {0x0102030405060708,
                                            0x0102030405060708};
  static const int64_t int64_values[2] = {int64_t(0xf1f2f3f4f5f6f7f8),
                                          int64_t(0xf1f2f3f4f5f6f7f8)};
  uint8_t buffer[8];

  MOZ_RELEASE_ASSERT(LittleEndian::readUint16(&unsigned_bytes[0]) == 0x0201);
  MOZ_RELEASE_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x0102);

  MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&unsigned_bytes[0]) ==
                     0x04030201U);
  MOZ_RELEASE_ASSERT(BigEndian::readUint32(&unsigned_bytes[0]) == 0x01020304U);

  MOZ_RELEASE_ASSERT(LittleEndian::readUint64(&unsigned_bytes[0]) ==
                     0x0807060504030201ULL);
  MOZ_RELEASE_ASSERT(BigEndian::readUint64(&unsigned_bytes[0]) ==
                     0x0102030405060708ULL);

  if (sizeof(uintptr_t) == 8) {
    // MSVC warning C4309 is "'static_cast': truncation of constant value" and
    // will hit for the literal casts below in 32-bit builds -- in dead code,
    // because only the other arm of this |if| runs.  Turn off the warning for
    // these two uses in dead code.
#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable : 4309)
#endif
    MOZ_RELEASE_ASSERT(LittleEndian::readUintptr(&unsigned_bytes[0]) ==
                       static_cast<uintptr_t>(0x0807060504030201ULL));
    MOZ_RELEASE_ASSERT(BigEndian::readUintptr(&unsigned_bytes[0]) ==
                       static_cast<uintptr_t>(0x0102030405060708ULL));
#ifdef _MSC_VER
#  pragma warning(pop)
#endif
  } else {
    MOZ_RELEASE_ASSERT(LittleEndian::readUintptr(&unsigned_bytes[0]) ==
                       0x04030201U);
    MOZ_RELEASE_ASSERT(BigEndian::readUintptr(&unsigned_bytes[0]) ==
                       0x01020304U);
  }

  LittleEndian::writeUint16(&buffer[0], 0x0201);
  MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) ==
                     0);
  BigEndian::writeUint16(&buffer[0], 0x0102);
  MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) ==
                     0);

  LittleEndian::writeUint32(&buffer[0], 0x04030201U);
  MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) ==
                     0);
  BigEndian::writeUint32(&buffer[0], 0x01020304U);
  MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) ==
                     0);

  LittleEndian::writeUint64(&buffer[0], 0x0807060504030201ULL);
  MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) ==
                     0);
  BigEndian::writeUint64(&buffer[0], 0x0102030405060708ULL);
  MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) ==
                     0);

  memset(&buffer[0], 0xff, sizeof(buffer));
  LittleEndian::writeUintptr(&buffer[0], uintptr_t(0x0807060504030201ULL));
  MOZ_RELEASE_ASSERT(
      memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uintptr_t)) == 0);
  if (sizeof(uintptr_t) == 4) {
    MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
  }

  memset(&buffer[0], 0xff, sizeof(buffer));
  if (sizeof(uintptr_t) == 8) {
    BigEndian::writeUintptr(&buffer[0], uintptr_t(0x0102030405060708ULL));
  } else {
    BigEndian::writeUintptr(&buffer[0], uintptr_t(0x01020304U));
    MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
  }
  MOZ_RELEASE_ASSERT(
      memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uintptr_t)) == 0);

  MOZ_RELEASE_ASSERT(LittleEndian::readInt16(&signed_bytes[0]) ==
                     int16_t(0xf2f1));
  MOZ_RELEASE_ASSERT(BigEndian::readInt16(&signed_bytes[0]) == int16_t(0xf1f2));

  MOZ_RELEASE_ASSERT(LittleEndian::readInt32(&signed_bytes[0]) ==
                     int32_t(0xf4f3f2f1));
  MOZ_RELEASE_ASSERT(BigEndian::readInt32(&signed_bytes[0]) ==
                     int32_t(0xf1f2f3f4));

  MOZ_RELEASE_ASSERT(LittleEndian::readInt64(&signed_bytes[0]) ==
                     int64_t(0xf8f7f6f5f4f3f2f1LL));
  MOZ_RELEASE_ASSERT(BigEndian::readInt64(&signed_bytes[0]) ==
                     int64_t(0xf1f2f3f4f5f6f7f8LL));

  if (sizeof(uintptr_t) == 8) {
    MOZ_RELEASE_ASSERT(LittleEndian::readIntptr(&signed_bytes[0]) ==
                       intptr_t(0xf8f7f6f5f4f3f2f1LL));
    MOZ_RELEASE_ASSERT(BigEndian::readIntptr(&signed_bytes[0]) ==
                       intptr_t(0xf1f2f3f4f5f6f7f8LL));
  } else {
    MOZ_RELEASE_ASSERT(LittleEndian::readIntptr(&signed_bytes[0]) ==
                       intptr_t(0xf4f3f2f1));
    MOZ_RELEASE_ASSERT(BigEndian::readIntptr(&signed_bytes[0]) ==
                       intptr_t(0xf1f2f3f4));
  }

  LittleEndian::writeInt16(&buffer[0], int16_t(0xf2f1));
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) ==
                     0);
  BigEndian::writeInt16(&buffer[0], int16_t(0xf1f2));
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) ==
                     0);

  LittleEndian::writeInt32(&buffer[0], 0xf4f3f2f1);
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) ==
                     0);
  BigEndian::writeInt32(&buffer[0], 0xf1f2f3f4);
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) ==
                     0);

  LittleEndian::writeInt64(&buffer[0], 0xf8f7f6f5f4f3f2f1LL);
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) ==
                     0);
  BigEndian::writeInt64(&buffer[0], 0xf1f2f3f4f5f6f7f8LL);
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) ==
                     0);

  memset(&buffer[0], 0xff, sizeof(buffer));
  LittleEndian::writeIntptr(&buffer[0], intptr_t(0xf8f7f6f5f4f3f2f1LL));
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(intptr_t)) ==
                     0);
  if (sizeof(intptr_t) == 4) {
    MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
  }

  memset(&buffer[0], 0xff, sizeof(buffer));
  if (sizeof(intptr_t) == 8) {
    BigEndian::writeIntptr(&buffer[0], intptr_t(0xf1f2f3f4f5f6f7f8LL));
  } else {
    BigEndian::writeIntptr(&buffer[0], intptr_t(0xf1f2f3f4));
    MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
  }
  MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(intptr_t)) ==
                     0);

  TestSingleSwap(uint16_t(0xf2f1), uint16_t(0xf1f2));
  TestSingleSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf1f2f3f4));
  TestSingleSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf1f2f3f4f5f6f7f8));

  TestSingleSwap(int16_t(0xf2f1), int16_t(0xf1f2));
  TestSingleSwap(int32_t(0xf4f3f2f1), int32_t(0xf1f2f3f4));
  TestSingleSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf1f2f3f4f5f6f7f8));

  TestSingleNoSwap(uint16_t(0xf2f1), uint16_t(0xf2f1));
  TestSingleNoSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf4f3f2f1));
  TestSingleNoSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf8f7f6f5f4f3f2f1));

  TestSingleNoSwap(int16_t(0xf2f1), int16_t(0xf2f1));
  TestSingleNoSwap(int32_t(0xf4f3f2f1), int32_t(0xf4f3f2f1));
  TestSingleNoSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf8f7f6f5f4f3f2f1));

  TestBulkSwap(uint16_values);
  TestBulkSwap(int16_values);
  TestBulkSwap(uint32_values);
  TestBulkSwap(int32_values);
  TestBulkSwap(uint64_values);
  TestBulkSwap(int64_values);

  TestBulkNoSwap(uint16_values);
  TestBulkNoSwap(int16_values);
  TestBulkNoSwap(uint32_values);
  TestBulkNoSwap(int32_values);
  TestBulkNoSwap(uint64_values);
  TestBulkNoSwap(int64_values);

  TestBulkInPlaceSwap(uint16_values);
  TestBulkInPlaceSwap(int16_values);
  TestBulkInPlaceSwap(uint32_values);
  TestBulkInPlaceSwap(int32_values);
  TestBulkInPlaceSwap(uint64_values);
  TestBulkInPlaceSwap(int64_values);

  TestBulkInPlaceNoSwap(uint16_values);
  TestBulkInPlaceNoSwap(int16_values);
  TestBulkInPlaceNoSwap(uint32_values);
  TestBulkInPlaceNoSwap(int32_values);
  TestBulkInPlaceNoSwap(uint64_values);
  TestBulkInPlaceNoSwap(int64_values);

  return 0;
}
