diff --git a/externals/coda-oss/.github/workflows/build_unittest.yml b/externals/coda-oss/.github/workflows/build_unittest.yml index 68c6a6bb2f..3f7b581659 100644 --- a/externals/coda-oss/.github/workflows/build_unittest.yml +++ b/externals/coda-oss/.github/workflows/build_unittest.yml @@ -46,10 +46,10 @@ jobs: - name: test run: | cd target-Release - ctest -C Release + ctest -C Release --output-on-failure cd .. cd target-Debug - ctest -C Debug + ctest -C Debug --output-on-failure build-linux-cmake: strategy: @@ -86,7 +86,7 @@ jobs: - name: test run: | cd target - ctest + ctest --output-on-failure build-waf: strategy: diff --git a/externals/coda-oss/modules/c++/gsl/include/gsl/Gsl_.h b/externals/coda-oss/modules/c++/gsl/include/gsl/Gsl_.h index 46ef55ddf5..711458fed4 100644 --- a/externals/coda-oss/modules/c++/gsl/include/gsl/Gsl_.h +++ b/externals/coda-oss/modules/c++/gsl/include/gsl/Gsl_.h @@ -29,7 +29,7 @@ #include "gsl/Gsl_narrow.h" #include "gsl/use_gsl.h" // Can't compile all of GSL with older versions of GCC/MSVC -#if !CODA_OSS_gsl_use_real_gsl_ +#if !CODA_OSS_use_real_gsl_ // Add to "gsl" if we're not using the real thing namespace gsl { @@ -45,6 +45,6 @@ namespace gsl return Gsl::narrow(u); } } -#endif // CODA_OSS_gsl_use_real_gsl_ +#endif // CODA_OSS_coda_oss_use_real_gsl_ #endif // CODA_OSS_gsl_Gsl__h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/gsl/include/gsl/gsl.h b/externals/coda-oss/modules/c++/gsl/include/gsl/gsl.h index 45ed51505d..bd7b33a097 100644 --- a/externals/coda-oss/modules/c++/gsl/include/gsl/gsl.h +++ b/externals/coda-oss/modules/c++/gsl/include/gsl/gsl.h @@ -32,9 +32,10 @@ // always compile Gsl (not "gsl") code--our own simple implementation #include "gsl/Gsl_.h" // our own "fake" GSL -#if CODA_OSS_gsl_use_real_gsl_ +#if CODA_OSS_use_real_gsl_ CODA_OSS_disable_warning_push #if _MSC_VER + #pragma warning(disable: 4619) // #pragma warning : there is no warning number '..' #pragma warning(disable: 4626) // '...' : assignment operator was implicitly defined as deleted #pragma warning(disable: 5027) // '...' : move assignment operator was implicitly defined as deleted #pragma warning(disable: 26487) // Don 't return a pointer '...' that may be invalid (lifetime.4). diff --git a/externals/coda-oss/modules/c++/gsl/include/gsl/use_gsl.h b/externals/coda-oss/modules/c++/gsl/include/gsl/use_gsl.h index c19faaae25..8135edf1d8 100644 --- a/externals/coda-oss/modules/c++/gsl/include/gsl/use_gsl.h +++ b/externals/coda-oss/modules/c++/gsl/include/gsl/use_gsl.h @@ -24,17 +24,17 @@ #pragma once // Need a fairly decent C++ compiler to use the real GSL -#ifndef CODA_OSS_coda_oss_use_real_gsl_ +#ifndef CODA_OSS_use_real_gsl_ #if defined(_MSC_VER) // need VS2017 or later to compile the real GSL code - #define CODA_OSS_coda_oss_use_real_gsl_ (_MSC_VER >= 1910) // VS2017: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-160 + #define CODA_OSS_use_real_gsl_ (_MSC_VER >= 1910) // VS2017: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-160 #elif defined (__GNUC__) // GCC 4.9.1 and 4.9.4 won't compile GSL - #define CODA_OSS_coda_oss_use_real_gsl_ (__GNUC__ >= 5) + #define CODA_OSS_use_real_gsl_ (__GNUC__ >= 5) #else // assume GSL can be compiled with any C++14 compiler #include "coda_oss/CPlusPlus.h" - #define CODA_OSS_coda_oss_use_real_gsl_ CODA_OSS_cpp14 + #define CODA_OSS_use_real_gsl_ CODA_OSS_cpp14 #endif #endif diff --git a/externals/coda-oss/modules/c++/str/source/EncodedStringView.cpp b/externals/coda-oss/modules/c++/str/source/EncodedStringView.cpp index 90fcf31441..a80340a88d 100644 --- a/externals/coda-oss/modules/c++/str/source/EncodedStringView.cpp +++ b/externals/coda-oss/modules/c++/str/source/EncodedStringView.cpp @@ -66,7 +66,7 @@ static std::string to_native(coda_oss::u8string::const_pointer p, size_t sz) } if (Platform == PlatformType::Linux) { - return str::cast(p); // copy + return std::string(str::cast(p), sz); } throw std::logic_error("Unknown platform."); } @@ -75,7 +75,7 @@ static std::string to_native(str::W1252string::const_pointer p, size_t sz) { if (Platform == PlatformType::Windows) { - return str::cast(p); // copy + return std::string(str::cast(p), sz); } if (Platform == PlatformType::Linux) { diff --git a/externals/coda-oss/modules/c++/sys/include/sys/Conf.h b/externals/coda-oss/modules/c++/sys/include/sys/Conf.h index ad5a1e4c99..28d396c5c3 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Conf.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Conf.h @@ -66,12 +66,20 @@ #include #include #include +#include +#ifdef __GNUC__ +#include // "These functions are GNU extensions." +#endif #include #include #include #include #include +#include +#include +#include "coda_oss/span.h" +#include #include "str/Format.h" #include "sys/TimeStamp.h" @@ -208,29 +216,38 @@ namespace sys * \param elemSize * \param numElems */ - inline void byteSwap(void* buffer, - unsigned short elemSize, - size_t numElems) + void CODA_OSS_API byteSwap_(void* buffer, size_t elemSize, size_t numElems); + template + inline void byteSwap(T* buffer, size_t elemSize, size_t numElems) { - sys::byte* bufferPtr = static_cast(buffer); - if (!bufferPtr || elemSize < 2 || !numElems) - return; - - const auto half = elemSize >> 1; - size_t offset = 0, innerOff = 0, innerSwap = 0; - - for(size_t i = 0; i < numElems; ++i, offset += elemSize) + // Trying to byte-swap structs can result in garbage because of padding. + static_assert(std::is_arithmetic::value || std::is_enum::value, "can only byte-swap numbers."); + if (elemSize != sizeof(T)) { - for(unsigned short j = 0; j < half; ++j) - { - innerOff = offset + j; - innerSwap = offset + elemSize - 1 - j; - - std::swap(bufferPtr[innerOff], bufferPtr[innerSwap]); - } + throw std::invalid_argument("sizeof(T) != elemSize"); } + byteSwap_(buffer, elemSize, numElems); + } + template + inline void byteSwap(coda_oss::span buffer) + { + constexpr auto elemSize = sizeof(T); + const auto numElems = buffer.size(); + byteSwap(buffer.data(), elemSize, numElems); + } + template + inline void byteSwap(coda_oss::span> buffer) + { + void* pBuffer = buffer.data(); + const coda_oss::span buffer_(static_cast(pBuffer), buffer.size() * 2); // real and imag + byteSwap(buffer_); } + inline void byteSwapV(void* buffer, unsigned short elemSize, size_t numElems) // existing API + { + byteSwap_(buffer, elemSize, numElems); + } + /*! * Swap bytes into output buffer. Note that a complex pixel * is equivalent to two floats so elemSize and numElems @@ -241,33 +258,47 @@ namespace sys * \param numElems * \param[out] outputBuffer buffer to write swapped elements to */ - inline void byteSwap(const void* buffer, - unsigned short elemSize, - size_t numElems, - void* outputBuffer) + void CODA_OSS_API byteSwap_(const void* buffer, size_t elemSize, size_t numElems, void* outputBuffer); + template + inline void byteSwap(const T* buffer, size_t elemSize, size_t numElems, + U* outputBuffer) // e.g., "unsigned int" && "int" { - const sys::byte* bufferPtr = static_cast(buffer); - sys::byte* outputBufferPtr = static_cast(outputBuffer); - - if (!numElems || !bufferPtr || !outputBufferPtr) + // Trying to byte-swap structs can result in garbage because of padding. + static_assert(std::is_arithmetic::value || std::is_enum::value, "can only byte-swap numbers."); + static_assert(std::is_arithmetic::value || std::is_enum::value, "can only byte-swap numbers."); + static_assert(sizeof(T) > 1, "byte-swapping a single-byte value makes no sense."); + //static_assert(sizeof(T) == sizeof(U), "sizeof(T) != sizeof(U)."); // outputBuffer could be std::byte + if (elemSize != sizeof(T)) { - return; + throw std::invalid_argument("sizeof(T) != elemSize"); } - - const auto half = elemSize >> 1; - size_t offset = 0; - - for (size_t ii = 0; ii < numElems; ++ii, offset += elemSize) + byteSwap_(buffer, elemSize, numElems, outputBuffer); + } + template + inline void byteSwap(coda_oss::span buffer, coda_oss::span outputBuffer) // e.g., "unsigned int" && "int" + { + const auto numElems = buffer.size(); + if (numElems != outputBuffer.size()) { - for (unsigned short jj = 0; jj < half; ++jj) - { - const size_t innerOff = offset + jj; - const size_t innerSwap = offset + elemSize - 1 - jj; - - outputBufferPtr[innerOff] = bufferPtr[innerSwap]; - outputBufferPtr[innerSwap] = bufferPtr[innerOff]; - } + throw std::invalid_argument("buffer.size() != outputBuffer.size()"); } + constexpr auto elemSize = sizeof(T); + byteSwap(buffer.data(), elemSize, numElems, outputBuffer.data()); + } + template + inline void byteSwap(coda_oss::span> buffer, coda_oss::span> outputBuffer) + { + const void* pBuffer = buffer.data(); + const coda_oss::span buffer_(static_cast(pBuffer), buffer.size() * 2); // real and imag + void* pOutputBuffer = outputBuffer.data(); + const coda_oss::span outputBuffer_(static_cast(pOutputBuffer), outputBuffer.size() * 2); // real and imag + + byteSwap(buffer_, outputBuffer_); + } + + inline void byteSwapV(const void* buffer, unsigned short elemSize, size_t numElems, void* outputBuffer) // existing API + { + byteSwap_(buffer, elemSize, numElems, outputBuffer); } /*! @@ -290,9 +321,12 @@ namespace sys * \endcode * */ - template T byteSwap(T val) + template inline T byteSwap_(T val) { - size_t size = sizeof(T); + // Trying to byte-swap structs can result in garbage because of padding. + static_assert(std::is_arithmetic::value || std::is_enum::value, "can only byte-swap numbers"); + + constexpr auto size = sizeof(T); T out; unsigned char* cOut = reinterpret_cast(&out); @@ -304,7 +338,71 @@ namespace sys } return out; } - + template inline T byteSwap(T val) + { + return byteSwap_(val); + } + inline uint8_t byteSwap(uint8_t val) + { + return val; // no-op + } +#if defined(_MSC_VER) + // These routines should geneerate a single instruction; see https://devblogs.microsoft.com/cppblog/a-tour-of-4-msvc-backend-improvements/ + inline uint16_t byteSwap(uint16_t val) + { + return _byteswap_ushort(val); + } + inline uint32_t byteSwap(uint32_t val) + { + return _byteswap_ulong(val); + } + inline uint64_t byteSwap(uint64_t val) + { + return _byteswap_uint64(val); + } +#elif defined(__GNUC__) + inline uint16_t byteSwap(uint16_t val) + { + return bswap_16(val); + } + inline uint32_t byteSwap(uint32_t val) + { + return bswap_32(val); + } + inline uint64_t byteSwap(uint64_t val) + { + return bswap_64(val); + } +#endif + template + inline T byteSwapValue_(T val) + { + static_assert(sizeof(T) > 1, "byte-swapping a single-byte value makes no sense."); + static_assert(sizeof(T) == sizeof(TUInt), "sizeof(T) != sizeof()"); + static_assert(std::is_unsigned::value, "TUInt must be 'unsigned'"); + + const void* pVal = &val; + const auto pUInt = static_cast(pVal); + const auto result = byteSwap(*pUInt); + + const void* pResult = &result; + const auto pRetval = static_cast(pResult); + return *pRetval; + } + inline float byteSwap(float val) + { + return byteSwapValue_(val); + } + inline double byteSwap(double val) + { + return byteSwapValue_(val); + } + template + inline std::complex byteSwap(std::complex v) + { + std::complex retval{byteSwap(v.real()), byteSwap(v.imag())}; + return retval; + } /*! * Method to create a block of memory on an alignment diff --git a/externals/coda-oss/modules/c++/sys/source/Conf.cpp b/externals/coda-oss/modules/c++/sys/source/Conf.cpp index c8560c7f3a..414374d5ab 100644 --- a/externals/coda-oss/modules/c++/sys/source/Conf.cpp +++ b/externals/coda-oss/modules/c++/sys/source/Conf.cpp @@ -20,8 +20,12 @@ * */ +#include + #include #include +#include +#include #include "sys/Conf.h" #include "coda_oss/bit.h" @@ -64,3 +68,88 @@ bool sys::isBigEndianSystem() } return retval; } + + + /*! + * Swap bytes in-place. Note that a complex pixel + * is equivalent to two floats so elemSize and numElems + * must be adjusted accordingly. + * + * \param [inout] buffer to transform + * \param elemSize + * \param numElems + */ +void sys::byteSwap_(void* buffer, size_t elemSize, size_t numElems) +{ + byteSwap_(buffer, elemSize, numElems, buffer); +} + + /*! + * Swap bytes into output buffer. Note that a complex pixel + * is equivalent to two floats so elemSize and numElems + * must be adjusted accordingly. + * + * \param buffer to transform + * \param elemSize + * \param numElems + * \param[out] outputBuffer buffer to write swapped elements to + */ +template +inline void byteSwap_n(const void *buffer_, size_t elemSize, size_t numElems, void *outputBuffer_) +{ + static_assert(std::is_unsigned::value, "TUInt must be 'unsigned'"); + using value_type = TUInt; + assert(sizeof(value_type) == elemSize); + std::ignore = elemSize; + + const coda_oss::span buffer(static_cast(buffer_), numElems); + assert(buffer.size_bytes() == elemSize * numElems); + const coda_oss::span outputBuffer(static_cast(outputBuffer_), numElems); + + std::transform(buffer.begin(), buffer.end(), outputBuffer.begin(), [](const auto& v) { return sys::byteSwap(v); }); +} + +void sys::byteSwap_(const void* buffer, + size_t elemSize, + size_t numElems, + void* outputBuffer) +{ + if (!numElems || !buffer || !outputBuffer) + { + return; + } + + if (elemSize == 2) + { + return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + } + if (elemSize == 4) + { + return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + } + if (elemSize == 8) + { + return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + } + + const sys::byte* bufferPtr = static_cast(buffer); + sys::byte* outputBufferPtr = static_cast(outputBuffer); + + const auto half = elemSize >> 1; + size_t offset = 0; + + for (size_t ii = 0; ii < numElems; ++ii, offset += elemSize) + { + for (unsigned short jj = 0; jj < half; ++jj) + { + const size_t innerOff = offset + jj; + const size_t innerSwap = offset + elemSize - 1 - jj; + + // could be the same buffer, see overload above + const auto bufferInner = bufferPtr[innerSwap]; + const auto bufferOff = bufferPtr[innerOff]; + outputBufferPtr[innerOff] = bufferInner; + outputBufferPtr[innerSwap] = bufferOff; + } + } +} diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_byte_swap.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_byte_swap.cpp index 908eae3e33..6ef347cb3f 100644 --- a/externals/coda-oss/modules/c++/sys/unittests/test_byte_swap.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_byte_swap.cpp @@ -22,10 +22,12 @@ #include "TestCase.h" -#include +#include +#include #include // std::endian -#include +#include // std::byte +#include #include @@ -101,8 +103,287 @@ TEST_CASE(testByteSwap) } } +#define CODA_OSS_define_byte(v) constexpr static std::byte v = static_cast(0 ## v) +CODA_OSS_define_byte(x00); +CODA_OSS_define_byte(x11); +CODA_OSS_define_byte(x22); +CODA_OSS_define_byte(x33); +CODA_OSS_define_byte(x44); +CODA_OSS_define_byte(x55); +CODA_OSS_define_byte(x66); +CODA_OSS_define_byte(x77); +CODA_OSS_define_byte(x88); +CODA_OSS_define_byte(x99); +CODA_OSS_define_byte(xAA); +CODA_OSS_define_byte(xBB); +CODA_OSS_define_byte(xCC); +CODA_OSS_define_byte(xDD); +CODA_OSS_define_byte(xEE); +CODA_OSS_define_byte(xFF); +#undef CODA_OSS_define_byte + +static constexpr std::byte two_bytes[]{x00, xFF}; +static constexpr std::byte four_bytes[]{x00, x11, xEE, xFF}; +static constexpr std::byte eight_bytes[]{x00, x11, x22, x33, xCC, xDD, xEE, xFF}; +static constexpr std::byte sixteen_bytes[]{x00, x11, x22, x33, x44, x55, x66, x77, x88, x99, xAA, xBB, xCC, xDD, xEE, xFF}; + +TEST_CASE(testByteSwapUInt16) +{ + const void* pBytes = &(two_bytes[0]); + auto pUInt16 = static_cast(pBytes); + auto swap = sys::byteSwap(*pUInt16); + TEST_ASSERT_NOT_EQ(*pUInt16, swap); + const void* pResult_ = &swap; + auto pResultBytes = static_cast(pResult_); + TEST_ASSERT(pResultBytes[0] == two_bytes[1]); + TEST_ASSERT(pResultBytes[1] == two_bytes[0]); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(*pUInt16, swap); + + // array swap from input to output + pBytes = &(sixteen_bytes[0]); + const auto buffer = static_cast(pBytes); + std::array outputBuffer; + sys::byteSwap(buffer, sizeof(uint16_t), outputBuffer.size(), outputBuffer.data()); + for (auto&& v : outputBuffer) + { + swap = sys::byteSwap(v); + TEST_ASSERT_NOT_EQ(v, swap); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(v, swap); + } + + // in-place swap + sys::byteSwap(outputBuffer.data(), sizeof(uint16_t), outputBuffer.size()); + pBytes = outputBuffer.data(); + pResultBytes = static_cast(pBytes); + for (size_t i=0; i<16; i++) + { + TEST_ASSERT(pResultBytes[i] == sixteen_bytes[i]); + } +} + +TEST_CASE(testByteSwapUInt32) +{ + const void* pBytes = &(four_bytes[0]); + auto pUInt32 = static_cast(pBytes); + auto swap = sys::byteSwap(*pUInt32); + TEST_ASSERT_NOT_EQ(*pUInt32, swap); + const void* pResult_ = &swap; + auto pResultBytes = static_cast(pResult_); + TEST_ASSERT(pResultBytes[0] == four_bytes[3]); + TEST_ASSERT(pResultBytes[1] == four_bytes[2]); + TEST_ASSERT(pResultBytes[2] == four_bytes[1]); + TEST_ASSERT(pResultBytes[3] == four_bytes[0]); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(*pUInt32, swap); + + // array swap from input to output + pBytes = &(sixteen_bytes[0]); + const auto buffer = static_cast(pBytes); + std::array outputBuffer; + sys::byteSwap(buffer, sizeof(uint32_t), outputBuffer.size(), outputBuffer.data()); + for (auto&& v : outputBuffer) + { + swap = sys::byteSwap(v); + TEST_ASSERT_NOT_EQ(v, swap); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(v, swap); + } + + // in-place swap + sys::byteSwap(outputBuffer.data(), sizeof(uint32_t), outputBuffer.size()); + pBytes = outputBuffer.data(); + pResultBytes = static_cast(pBytes); + for (size_t i=0; i<16; i++) + { + TEST_ASSERT(pResultBytes[i] == sixteen_bytes[i]); + } +} + +TEST_CASE(testByteSwapUInt64) +{ + const void* pBytes = &(eight_bytes[0]); + auto pUInt64 = static_cast(pBytes); + auto swap = sys::byteSwap(*pUInt64); + TEST_ASSERT_NOT_EQ(*pUInt64, swap); + const void* pResult_ = &swap; + auto pResultBytes = static_cast(pResult_); + TEST_ASSERT(pResultBytes[0] == eight_bytes[7]); + TEST_ASSERT(pResultBytes[1] == eight_bytes[6]); + TEST_ASSERT(pResultBytes[2] == eight_bytes[5]); + TEST_ASSERT(pResultBytes[3] == eight_bytes[4]); + TEST_ASSERT(pResultBytes[4] == eight_bytes[3]); + TEST_ASSERT(pResultBytes[5] == eight_bytes[2]); + TEST_ASSERT(pResultBytes[6] == eight_bytes[1]); + TEST_ASSERT(pResultBytes[7] == eight_bytes[0]); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(*pUInt64, swap); + + // array swap from input to output + pBytes = &(sixteen_bytes[0]); + const auto buffer = static_cast(pBytes); + std::array outputBuffer; + sys::byteSwap(buffer, sizeof(uint64_t), outputBuffer.size(), outputBuffer.data()); + for (auto&& v : outputBuffer) + { + swap = sys::byteSwap(v); + TEST_ASSERT_NOT_EQ(v, swap); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(v, swap); + } + + // in-place swap + sys::byteSwap(outputBuffer.data(), sizeof(uint64_t), outputBuffer.size()); + pBytes = outputBuffer.data(); + pResultBytes = static_cast(pBytes); + for (size_t i=0; i<16; i++) + { + TEST_ASSERT(pResultBytes[i] == sixteen_bytes[i]); + } +} + +TEST_CASE(testByteSwapFloat) +{ + static_assert(sizeof(float) == sizeof(uint32_t), "sizeof(float) != sizeof(uint32_t)"); + + const float v = 3.141592654f; + const void* pVoid = &v; + auto pFloat = static_cast(pVoid); + auto swap = sys::byteSwap(*pFloat); + // The swapped bits could be nonsense as a `float`, so comparing might not work + //TEST_ASSERT_NOT_EQ(*pFloat, swap); + const void* pResult_ = &swap; + auto pResultBytes = static_cast(pResult_); + auto pValueBytes = static_cast(pVoid); + TEST_ASSERT(pResultBytes[0] == pValueBytes[3]); + TEST_ASSERT(pResultBytes[1] == pValueBytes[2]); + TEST_ASSERT(pResultBytes[2] == pValueBytes[1]); + TEST_ASSERT(pResultBytes[3] == pValueBytes[0]); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(*pFloat, swap); + + // array swap from input to output + const float values[] = {1.0f, 2.0f, 3.0f, 4.0f}; + pVoid = &(values[0]); + const auto buffer = static_cast(pVoid); + std::array outputBuffer; + sys::byteSwap(buffer, sizeof(float), outputBuffer.size(), outputBuffer.data()); + for (size_t i = 0; i(pVoid); + auto swap = sys::byteSwap(*pDouble); + // The swapped bits could be nonsense as a `double`, so comparing might not work + //TEST_ASSERT_NOT_EQ(*pDouble, swap); + const void* pResult_ = &swap; + auto pResultBytes = static_cast(pResult_); + auto pValueBytes = static_cast(pVoid); + TEST_ASSERT(pResultBytes[0] == pValueBytes[7]); + TEST_ASSERT(pResultBytes[1] == pValueBytes[6]); + TEST_ASSERT(pResultBytes[2] == pValueBytes[5]); + TEST_ASSERT(pResultBytes[3] == pValueBytes[4]); + TEST_ASSERT(pResultBytes[4] == pValueBytes[3]); + TEST_ASSERT(pResultBytes[5] == pValueBytes[2]); + TEST_ASSERT(pResultBytes[6] == pValueBytes[1]); + TEST_ASSERT(pResultBytes[7] == pValueBytes[0]); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(*pDouble, swap); + + // array swap from input to output + const std::vector values = {1.0, 2.0}; + const std::span buffer(values.data(), values.size()); + std::array outputBuffer; + const std::span outputBuffer_(outputBuffer.data(), outputBuffer.size()); + sys::byteSwap(buffer, outputBuffer_); + for (size_t i = 0; i < outputBuffer_.size(); i++) + { + // can't test swapped bytes against anything; might not be a valid `double` + swap = sys::byteSwap(outputBuffer_[i]); + TEST_ASSERT_EQ(values[i], swap); + } + + // in-place swap + sys::byteSwap(outputBuffer_); + for (size_t i = 0; i < outputBuffer_.size(); i++) + { + TEST_ASSERT_EQ(values[i], outputBuffer_[i]); + } +} + +TEST_CASE(testByteSwapCxFloat) +{ + using value_type = std::complex; + static_assert(sizeof(value_type) == sizeof(uint64_t), "sizeof(value_type) != sizeof(uint64_t)"); + + const value_type v = {1.1f, -9.9f}; + const void* pVoid = &v; + auto pCxFloat = static_cast(pVoid); + auto swap = sys::byteSwap(*pCxFloat); + // The swapped bits could be nonsense as a `double`, so comparing might not work + //TEST_ASSERT_NOT_EQ(*pDouble, swap); + const void* pResult_ = &swap; + auto pResultBytes = static_cast(pResult_); + auto pValueBytes = static_cast(pVoid); + TEST_ASSERT(pResultBytes[0] == pValueBytes[3]); + TEST_ASSERT(pResultBytes[1] == pValueBytes[2]); + TEST_ASSERT(pResultBytes[2] == pValueBytes[1]); + TEST_ASSERT(pResultBytes[3] == pValueBytes[0]); + TEST_ASSERT(pResultBytes[4] == pValueBytes[7]); + TEST_ASSERT(pResultBytes[5] == pValueBytes[6]); + TEST_ASSERT(pResultBytes[6] == pValueBytes[5]); + TEST_ASSERT(pResultBytes[7] == pValueBytes[4]); + swap = sys::byteSwap(swap); // swap back + TEST_ASSERT_EQ(*pCxFloat, swap); + + // array swap from input to output + const std::vector values = {{1.1f, -9.9f}, {-22.22f, 88.88f}}; + const std::span buffer(values.data(), values.size()); + std::array outputBuffer; + const std::span outputBuffer_(outputBuffer.data(), outputBuffer.size()); + sys::byteSwap(buffer, outputBuffer_); + for (size_t i = 0; i 1) - sys::byteSwap(buffer, static_cast(elementSize), numElements); + sys::byteSwap(buffer, elementSize, numElements); } parseValues((const unsigned char *)buffer); diff --git a/externals/coda-oss/modules/c++/tiff/source/ImageReader.cpp b/externals/coda-oss/modules/c++/tiff/source/ImageReader.cpp index 2f7a95ad67..5fd42c6adc 100644 --- a/externals/coda-oss/modules/c++/tiff/source/ImageReader.cpp +++ b/externals/coda-oss/modules/c++/tiff/source/ImageReader.cpp @@ -76,7 +76,7 @@ void tiff::ImageReader::getData(unsigned char *buffer, throw except::Exception(Ctxt("Unsupported TIFF file format")); if (mReverseBytes) - sys::byteSwap((sys::byte*)buffer, mElementSize, numElementsToRead); + sys::byteSwap(buffer, mElementSize, numElementsToRead); } void tiff::ImageReader::getStripData(unsigned char *buffer, diff --git a/externals/coda-oss/modules/python/sys/source/generated/coda_sys_wrap.cxx b/externals/coda-oss/modules/python/sys/source/generated/coda_sys_wrap.cxx index 0a3ed4820c..f4b4930c02 100644 --- a/externals/coda-oss/modules/python/sys/source/generated/coda_sys_wrap.cxx +++ b/externals/coda-oss/modules/python/sys/source/generated/coda_sys_wrap.cxx @@ -3566,7 +3566,7 @@ SWIGINTERN PyObject *_wrap_byteSwap__SWIG_0(PyObject *SWIGUNUSEDPARM(self), PyOb SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "byteSwap" "', argument " "3"" of type '" "size_t""'"); } arg3 = static_cast< size_t >(val3); - sys::byteSwap(arg1,arg2,arg3); + sys::byteSwapV(arg1, arg2, arg3); resultobj = SWIG_Py_Void(); return resultobj; fail: @@ -3610,7 +3610,7 @@ SWIGINTERN PyObject *_wrap_byteSwap__SWIG_1(PyObject *SWIGUNUSEDPARM(self), PyOb if (!SWIG_IsOK(res4)) { SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "byteSwap" "', argument " "4"" of type '" "void *""'"); } - sys::byteSwap((void const *)arg1,arg2,arg3,arg4); + sys::byteSwapV((void const *)arg1,arg2,arg3,arg4); resultobj = SWIG_Py_Void(); return resultobj; fail: diff --git a/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp index 61230eaad1..c4177deb19 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp @@ -289,11 +289,11 @@ TEST_CASE(test_j2k_nitf_read_region) static std::vector readImage(nitf::ImageReader& imageReader, const nitf::ImageSubheader& imageSubheader) { - const int64_t numBlocks = imageSubheader.numBlocksPerRow() * imageSubheader.numBlocksPerCol(); - TEST_ASSERT_GREATER(numBlocks, 0); + const auto numBlocks = imageSubheader.numBlocksPerRow() * imageSubheader.numBlocksPerCol(); + TEST_ASSERT_GREATER(static_cast(numBlocks), 0); - const int64_t imageLength = imageSubheader.getNumBytesOfImageData(); - TEST_ASSERT_GREATER(imageLength, 0); + const auto imageLength = imageSubheader.getNumBytesOfImageData(); + TEST_ASSERT_GREATER(static_cast(imageLength), 0); // This assumes vertical blocking. // Interleaving would be required for horizontal blocks diff --git a/six/modules/c++/cphd/source/ByteSwap.cpp b/six/modules/c++/cphd/source/ByteSwap.cpp index f67a520129..2123e06aea 100644 --- a/six/modules/c++/cphd/source/ByteSwap.cpp +++ b/six/modules/c++/cphd/source/ByteSwap.cpp @@ -51,9 +51,8 @@ void byteSwap(const void* in, T& out) } } -class ByteSwapRunnable : public sys::Runnable +struct ByteSwapRunnable final : public sys::Runnable { -public: ByteSwapRunnable(void* buffer, size_t elemSize, size_t startElement, @@ -66,7 +65,7 @@ class ByteSwapRunnable : public sys::Runnable virtual void run() { - sys::byteSwap(mBuffer, mElemSize, mNumElements); + sys::byteSwapV(mBuffer, mElemSize, mNumElements); } private: @@ -82,9 +81,8 @@ inline const std::byte* calc_offset(const void* input_, size_t offset) } template -class ByteSwapAndPromoteRunnable : public sys::Runnable +struct ByteSwapAndPromoteRunnable final : public sys::Runnable { -public: ByteSwapAndPromoteRunnable(const void* input, size_t startRow, size_t numRows, @@ -103,9 +101,7 @@ class ByteSwapAndPromoteRunnable : public sys::Runnable for (size_t row = 0, inIdx = 0, outIdx = 0; row < mDims.row; ++row) { - for (size_t col = 0; - col < mDims.col; - ++col, inIdx += sizeof(std::complex), ++outIdx) + for (size_t col = 0; col < mDims.col; ++col, inIdx += sizeof(std::complex), ++outIdx) { // Have to be careful here - can't treat mInput as a // std::complex directly in case InT is a float (see @@ -114,8 +110,7 @@ class ByteSwapAndPromoteRunnable : public sys::Runnable byteSwap(input, real); byteSwap(calc_offset(input, sizeof(InT)), imag); - mOutput[outIdx] = std::complex(real, - imag); + mOutput[outIdx] = std::complex(real, imag); } } } @@ -261,7 +256,7 @@ void byteSwap(void* buffer, { if (numThreads <= 1) { - sys::byteSwap(buffer, + sys::byteSwapV(buffer, static_cast(elemSize), numElements); } diff --git a/six/modules/c++/six.sicd/source/SICDWriteControl.cpp b/six/modules/c++/six.sicd/source/SICDWriteControl.cpp index 4dc07cfb53..3ea49f7447 100644 --- a/six/modules/c++/six.sicd/source/SICDWriteControl.cpp +++ b/six/modules/c++/six.sicd/source/SICDWriteControl.cpp @@ -116,7 +116,7 @@ void SICDWriteControl::save(void* imageData, // Byte swap if needed if (doByteSwap) { - sys::byteSwap(imageData, + sys::byteSwapV(imageData, static_cast(numBytesPerPixel), numPixelsTotal); } @@ -181,7 +181,7 @@ void SICDWriteControl::save(void* imageData, // Byte swap back if needed if (doByteSwap && restoreData) { - sys::byteSwap(imageData, + sys::byteSwapV(imageData, static_cast(numBytesPerPixel), numPixelsTotal); } diff --git a/six/modules/c++/six.sicd/tests/test_sicd_byte_provider.cpp b/six/modules/c++/six.sicd/tests/test_sicd_byte_provider.cpp index 118d5f7f01..b79f8082cf 100644 --- a/six/modules/c++/six.sicd/tests/test_sicd_byte_provider.cpp +++ b/six/modules/c++/six.sicd/tests/test_sicd_byte_provider.cpp @@ -68,9 +68,8 @@ struct Tester final mBigEndianImage = mImage; if (std::endian::native == std::endian::little) { - sys::byteSwap(mBigEndianImage.data(), - sizeof(DataTypeT), - mBigEndianImage.size() * 2); + const std::span> buffer(mBigEndianImage.data(), mBigEndianImage.size()); + sys::byteSwap(buffer); } normalWrite(); diff --git a/six/modules/c++/six/include/six/Serialize.h b/six/modules/c++/six/include/six/Serialize.h index 665fec8192..578323a25f 100644 --- a/six/modules/c++/six/include/six/Serialize.h +++ b/six/modules/c++/six/include/six/Serialize.h @@ -55,12 +55,12 @@ struct Serializer { constexpr size_t length = sizeof(T); const void* pVal = &val; - auto data = static_cast::const_pointer>(pVal); if (swapBytes) { const size_t prevLength = buffer.size(); buffer.resize(prevLength + length); + auto data = static_cast(pVal); sys::byteSwap(data, static_cast(length), 1, @@ -68,6 +68,7 @@ struct Serializer } else { + auto data = static_cast::const_pointer>(pVal); std::copy(data, data + length, std::back_inserter(buffer)); } } @@ -99,15 +100,20 @@ struct Serializer std::copy(buffer, buffer + length, data); if (swapBytes) { - sys::byteSwap(data, static_cast(length), 1); + sys::byteSwap(static_cast(pVal), static_cast(length), 1); } buffer += length; } static void deserializeImpl(const std::byte*& buffer, bool swapBytes, T& val) { - auto& buffer_ = reinterpret_cast(buffer); + const void* pBuffer = buffer; + auto buffer_ = static_cast(pBuffer); + deserializeImpl(buffer_, swapBytes, val); + + pBuffer = buffer_; + buffer = static_cast(pBuffer); } }; diff --git a/six/modules/c++/six/source/Adapters.cpp b/six/modules/c++/six/source/Adapters.cpp index 19647c6952..cd0146e2a3 100644 --- a/six/modules/c++/six/source/Adapters.cpp +++ b/six/modules/c++/six/source/Adapters.cpp @@ -58,7 +58,7 @@ static inline void nitf_free(NITF_DATA* data) static inline void byteSwap(std::vector& buffer, size_t elemSize, size_t numElems) { assert(buffer.size() == elemSize * numElems); - sys::byteSwap(buffer.data(), gsl::narrow(elemSize), numElems); + sys::byteSwapV(buffer.data(), gsl::narrow(elemSize), numElems); } template static NITF_BOOL write(const TImpl* impl, nitf_IOInterface* io, nitf_Error* error, TWriteFunc write_f) diff --git a/six/modules/c++/six/unittests/test_serialize.cpp b/six/modules/c++/six/unittests/test_serialize.cpp index b5e182a878..56ac38d865 100644 --- a/six/modules/c++/six/unittests/test_serialize.cpp +++ b/six/modules/c++/six/unittests/test_serialize.cpp @@ -96,7 +96,7 @@ TEST_CASE(ScalarSerialize) // Test with no byte swapping TEST_ASSERT_TRUE(testScalar(false)); TEST_ASSERT_TRUE(testScalar(false)); - TEST_ASSERT_TRUE(testScalar(false)); + //TEST_ASSERT_TRUE(testScalar(false)); TEST_ASSERT_TRUE(testScalar(false)); TEST_ASSERT_TRUE(testScalar(false)); @@ -127,7 +127,7 @@ TEST_CASE(VectorSerialize) TEST_ASSERT_TRUE(testVector(length, false)); TEST_ASSERT_TRUE(testVector(length, false)); TEST_ASSERT_TRUE(testVector(length, false)); - TEST_ASSERT_TRUE(testVector(length, false)); + //TEST_ASSERT_TRUE(testVector(length, false)); // Test with byte swapping (do not test sys::byte) TEST_ASSERT_TRUE(testVector(length, true));