From 7998a723a60b708e57481adbfe83e8ed885280b7 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 30 May 2023 15:22:04 -0400 Subject: [PATCH] Squashed 'externals/nitro/' changes from 9e5cd6e6f..cdda3982c cdda3982c Merge branch 'main' into cpp17 2fd7a0bfa latest from coda-oss (#552) b660acc8d Merge branch 'main' into cpp17 842bcb3c7 Merge commit 'b4e35176b5f190a9783b8e02d2a6463163cea097' into cpp17 b4e35176b Squashed 'externals/coda-oss/' changes from 8abcb1825d..7b90b5a46b 73d3dbaab latest from coda-oss 0eecce004 invoke() utility to reduce code duplication (#550) 59fb02fe9 latest from coda-oss (#551) 9fbf2b7b8 Fill out adapter free block which is used for nitf decompression (#549) git-subtree-dir: externals/nitro git-subtree-split: cdda3982c7ac21827c8369faa8d8f4fc963e7900 --- externals/coda-oss/UnitTest/UnitTest.vcxproj | 6 +- .../UnitTest/UnitTest.vcxproj.filters | 3 + externals/coda-oss/UnitTest/mt.cpp | 4 + externals/coda-oss/UnitTest/pch.h | 2 + .../modules/c++/coda-oss-lite.vcxproj | 6 +- .../modules/c++/coda-oss-lite.vcxproj.filters | 12 + .../c++/coda_oss/include/coda_oss/CPlusPlus.h | 3 +- .../c++/coda_oss/include/coda_oss/bit.h | 60 +++ .../c++/coda_oss/include/coda_oss/cstddef.h | 4 + .../c++/coda_oss/include/coda_oss/span.h | 21 + .../modules/c++/mem/unittests/test_Span.cpp | 36 ++ .../c++/mt/include/mt/ThreadedByteSwap.h | 90 +++++ .../c++/mt/unittests/test_mt_byte_swap.cpp | 72 ++++ .../coda-oss/modules/c++/std/include/std/bit | 20 +- .../coda-oss/modules/c++/std/include/std/span | 3 + .../modules/c++/sys/include/sys/AbstractOS.h | 5 + .../modules/c++/sys/include/sys/ByteSwap.h | 118 ++++++ .../c++/sys/include/sys/ByteSwapValue.h | 196 ++++++++++ .../modules/c++/sys/include/sys/Conf.h | 208 +--------- .../modules/c++/sys/include/sys/Span.h | 188 +++++++++ .../modules/c++/sys/source/AbstractOS.cpp | 27 ++ .../coda-oss/modules/c++/sys/source/Conf.cpp | 261 ++++++++++--- .../c++/sys/unittests/test_byte_swap.cpp | 366 ++++++------------ .../xml.lite/include/xml/lite/Attributes.h | 14 + .../include/xml/lite/ValidatorXerces.h | 3 + .../c++/xml.lite/source/ValidatorXerces.cpp | 28 +- .../sys/source/generated/coda_sys_wrap.cxx | 4 +- .../include/nitf/DecompressionInterface.hpp | 8 +- .../nitf/source/DecompressionInterface.cpp | 112 +++--- 29 files changed, 1295 insertions(+), 585 deletions(-) create mode 100644 externals/coda-oss/modules/c++/mt/include/mt/ThreadedByteSwap.h create mode 100644 externals/coda-oss/modules/c++/mt/unittests/test_mt_byte_swap.cpp create mode 100644 externals/coda-oss/modules/c++/sys/include/sys/ByteSwap.h create mode 100644 externals/coda-oss/modules/c++/sys/include/sys/ByteSwapValue.h create mode 100644 externals/coda-oss/modules/c++/sys/include/sys/Span.h diff --git a/externals/coda-oss/UnitTest/UnitTest.vcxproj b/externals/coda-oss/UnitTest/UnitTest.vcxproj index 5f55f6360d..e6dd924ee7 100644 --- a/externals/coda-oss/UnitTest/UnitTest.vcxproj +++ b/externals/coda-oss/UnitTest/UnitTest.vcxproj @@ -25,7 +25,6 @@ v143 false Unicode - true DynamicLibrary @@ -34,7 +33,6 @@ true false Unicode - true @@ -259,6 +257,10 @@ true true + + true + true + true true diff --git a/externals/coda-oss/UnitTest/UnitTest.vcxproj.filters b/externals/coda-oss/UnitTest/UnitTest.vcxproj.filters index e85d2fcd02..37d7c36cf4 100644 --- a/externals/coda-oss/UnitTest/UnitTest.vcxproj.filters +++ b/externals/coda-oss/UnitTest/UnitTest.vcxproj.filters @@ -219,6 +219,9 @@ re + + mt + diff --git a/externals/coda-oss/UnitTest/mt.cpp b/externals/coda-oss/UnitTest/mt.cpp index 2de6f17fd6..6190f8aa4a 100644 --- a/externals/coda-oss/UnitTest/mt.cpp +++ b/externals/coda-oss/UnitTest/mt.cpp @@ -24,4 +24,8 @@ TEST_CLASS(work_sharing_balanced_runnable_1d_test){ public: #include "mt/unittests/work_sharing_balanced_runnable_1d_test.cpp" }; +TEST_CLASS(test_mt_byte_swap){ public: +#include "mt/unittests/test_mt_byte_swap.cpp" +}; + } \ No newline at end of file diff --git a/externals/coda-oss/UnitTest/pch.h b/externals/coda-oss/UnitTest/pch.h index 8229e0b47a..ecbdb7c880 100644 --- a/externals/coda-oss/UnitTest/pch.h +++ b/externals/coda-oss/UnitTest/pch.h @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,7 @@ #include #include #include +#include #include #include #include diff --git a/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj b/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj index f50ac4a193..d5e4e5cf81 100644 --- a/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj +++ b/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj @@ -149,6 +149,7 @@ + @@ -191,6 +192,8 @@ + + @@ -228,6 +231,7 @@ + @@ -421,14 +425,12 @@ DynamicLibrary true v143 - true DynamicLibrary false v143 true - true diff --git a/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj.filters b/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj.filters index 75b5514713..a8119ff835 100644 --- a/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj.filters +++ b/externals/coda-oss/modules/c++/coda-oss-lite.vcxproj.filters @@ -735,6 +735,18 @@ config + + sys + + + sys + + + mt + + + sys + diff --git a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h index 018be14ae3..8d5d6a0d32 100644 --- a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h +++ b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h @@ -63,11 +63,12 @@ #endif // CODA_OSS_cplusplus // Define a few macros as that's less verbose than testing against a version number +// https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros #define CODA_OSS_cpp11 (CODA_OSS_cplusplus >= 201103L) #define CODA_OSS_cpp14 (CODA_OSS_cplusplus >= 201402L) #define CODA_OSS_cpp17 (CODA_OSS_cplusplus >= 201703L) #define CODA_OSS_cpp20 (CODA_OSS_cplusplus >= 202002L) -#define CODA_OSS_cpp23 0 +#define CODA_OSS_cpp23 (CODA_OSS_cplusplus >= 202302L) #if !CODA_OSS_cpp17 #error "Must compile with C++17 or greater." diff --git a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/bit.h b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/bit.h index 99045bba17..32ef77862e 100644 --- a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/bit.h +++ b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/bit.h @@ -22,6 +22,14 @@ #define CODA_OSS_coda_oss_bit_h_INCLUDED_ #pragma once +#include +#include +#ifdef __GNUC__ +#include // "These functions are GNU extensions." +#endif + +#include + #include "coda_oss/namespace_.h" namespace coda_oss { @@ -38,6 +46,58 @@ namespace coda_oss native = __BYTE_ORDER__ #endif }; + + // https://en.cppreference.com/w/cpp/numeric/byteswap + namespace details + { + // Overloads for common types + inline constexpr uint8_t byteswap(uint8_t val) noexcept + { + return val; // no-op + } + #if defined(_MSC_VER) + // These routines should generate 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); + } + #else + #error "No platform-specific byteswap()" // TODO: do something else? + #endif + } + + template + inline T byteswap(T n) noexcept + { + // "std::byteswap participates in overload resolution only if T satisfies integral, i.e., T is an integer type. The program is + // ill-formed if T has padding bits." + static_assert(std::is_integral::value, "T must be integral"); + + using unsigned_t = std::make_unsigned_t; // "Since C++14" https://en.cppreference.com/w/cpp/types/make_unsigned + return details::byteswap(static_cast(n)); + } } #endif // CODA_OSS_coda_oss_bit_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/cstddef.h b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/cstddef.h index 53eb7ac33f..d9320e054b 100644 --- a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/cstddef.h +++ b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/cstddef.h @@ -22,7 +22,10 @@ #define CODA_OSS_coda_oss_cstddef_h_INCLUDED_ #pragma once +#include + #include +#include // Need a fairly decent C++ compiler to use the real GSL. This brings in more than // we really need for span (e.g., gsl::narrow()), but it keeps things simple. @@ -32,5 +35,6 @@ namespace coda_oss { using byte = std::byte; } +static_assert(!std::is_same::value, "'coda_oss::byte' should be a unique type."); #endif // CODA_OSS_coda_oss_cstddef_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/span.h b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/span.h index c33375c3e7..b225f35fe1 100644 --- a/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/span.h +++ b/externals/coda-oss/modules/c++/coda_oss/include/coda_oss/span.h @@ -25,6 +25,7 @@ #include "coda_oss/namespace_.h" #include "coda_oss/span_.h" +#include "coda_oss/cstddef.h" // byte // Need a fairly decent C++ compiler to use the real GSL. This brings in more than // we really need for span (e.g., gsl::narrow()), but it keeps things simple. @@ -37,6 +38,26 @@ namespace coda_oss #else // no gsl::span, use our own using details::span; #endif // GSL_SPAN_H + +// https://en.cppreference.com/w/cpp/container/span/as_bytes +template +span as_bytes(span s) noexcept +{ + const void* const p = s.data(); + return span(static_cast(p), s.size_bytes()); +} +template +span as_bytes(span s) noexcept +{ + return as_bytes(span(s.data(), s.size())); +} +template +span as_writable_bytes(span s) noexcept +{ + void* const p = s.data(); + return span(static_cast(p), s.size_bytes()); +} + } #endif // CODA_OSS_coda_oss_span_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/mem/unittests/test_Span.cpp b/externals/coda-oss/modules/c++/mem/unittests/test_Span.cpp index 472a879925..28a76ad203 100644 --- a/externals/coda-oss/modules/c++/mem/unittests/test_Span.cpp +++ b/externals/coda-oss/modules/c++/mem/unittests/test_Span.cpp @@ -26,6 +26,7 @@ #include #include +#include #include "TestCase.h" @@ -94,8 +95,43 @@ TEST_CASE(testGslNarrow) TEST_THROWS(gsl::narrow(d)); } +TEST_CASE(test_sys_make_span) +{ + int i = 314; + int* const p = &i; + auto s = sys::make_span(p, 1); + TEST_ASSERT(s.data() == p); + TEST_ASSERT_EQ(s[0], i); + s[0] = 123; + TEST_ASSERT_EQ(i, 123); + s[0] = 314; + + const int* const q = &i; + auto cs = sys::make_span(q, 1); + TEST_ASSERT(cs.data() == q); + TEST_ASSERT_EQ(cs[0], i); + //cs[0] = 123; // cs = span + TEST_ASSERT_EQ(i, 314); + + std::vector v{314}; + s = sys::make_span(v); + TEST_ASSERT(s.data() == v.data()); + TEST_ASSERT_EQ(s[0], v[0]); + s[0] = 123; + TEST_ASSERT_EQ(v[0], 123); + s[0] = 314; + + const std::vector& u = v; + cs = sys::make_span(u); + TEST_ASSERT(cs.data() == u.data()); + TEST_ASSERT_EQ(cs[0], u[0]); + // cs[0] = 123; // cs = span + TEST_ASSERT_EQ(u[0], 314); +} + TEST_MAIN( TEST_CHECK(testSpanBuffer); TEST_CHECK(testSpanVector); TEST_CHECK(testGslNarrow); + TEST_CHECK(test_sys_make_span); ) diff --git a/externals/coda-oss/modules/c++/mt/include/mt/ThreadedByteSwap.h b/externals/coda-oss/modules/c++/mt/include/mt/ThreadedByteSwap.h new file mode 100644 index 0000000000..0b97e1171d --- /dev/null +++ b/externals/coda-oss/modules/c++/mt/include/mt/ThreadedByteSwap.h @@ -0,0 +1,90 @@ +#ifndef CODA_OSS_mt_ThreadedByteSwap_h_INCLUDED_ +#define CODA_OSS_mt_ThreadedByteSwap_h_INCLUDED_ +#pragma once + +#include + +#include "sys/ByteSwap.h" + +#include "ThreadPlanner.h" +#include "ThreadGroup.h" + +namespace mt +{ +/* + * Threaded byte-swapping + * + * \param buffer Buffer to swap (contents will be overridden) + * \param elemSize Size of each element in 'buffer' + * \param numElements Number of elements in 'buffer' + * \param numThreads Number of threads to use for byte-swapping + */ +inline void threadedByteSwap(void* buffer, size_t elemSize, size_t numElements, size_t numThreads) +{ + if (numThreads <= 1) + { + sys::byteSwap(buffer, elemSize, numElements); + } + else + { + mt::ThreadGroup threads; + const mt::ThreadPlanner planner(numElements, numThreads); + + size_t threadNum(0); + size_t startElement(0); + size_t numElementsThisThread(0); + while (planner.getThreadInfo(threadNum++, startElement, numElementsThisThread)) + { + auto thread = std::make_unique( + buffer, + elemSize, + startElement, + numElementsThisThread); + + threads.createThread(thread.release()); + } + threads.joinAll(); + } +} + +/* + * Threaded byte-swapping and copy + * + * \param buffer Buffer to swap + * \param elemSize Size of each element in 'buffer' + * \param numElements Number of elements in 'buffer' + * \param numThreads Number of threads to use for byte-swapping + * \param outputBuffer buffer to write into + */ +inline void threadedByteSwap(const void* buffer, size_t elemSize, size_t numElements, size_t numThreads, void* outputBuffer) +{ + if (numThreads <= 1) + { + sys::byteSwap(buffer, elemSize, numElements, outputBuffer); + } + else + { + mt::ThreadGroup threads; + const mt::ThreadPlanner planner(numElements, numThreads); + + size_t threadNum(0); + size_t startElement(0); + size_t numElementsThisThread(0); + while (planner.getThreadInfo(threadNum++, startElement, numElementsThisThread)) + { + auto thread = std::make_unique( + buffer, + elemSize, + startElement, + numElementsThisThread, + outputBuffer); + + threads.createThread(thread.release()); + } + threads.joinAll(); + + } +} +} + +#endif // CODA_OSS_mt_ThreadedByteSwap_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/mt/unittests/test_mt_byte_swap.cpp b/externals/coda-oss/modules/c++/mt/unittests/test_mt_byte_swap.cpp new file mode 100644 index 0000000000..d2099cf83b --- /dev/null +++ b/externals/coda-oss/modules/c++/mt/unittests/test_mt_byte_swap.cpp @@ -0,0 +1,72 @@ +/* ========================================================================= + * This file is part of mt-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2017, MDA Information Systems LLC + * + * sys-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ + +#include "TestCase.h" + +#include + +#include +#include // std::byte +#include + +#include + +static std::vector make_origValues(size_t NUM_PIXELS) +{ + ::srand(334); + + std::vector retval(NUM_PIXELS); + for (size_t ii = 0; ii < NUM_PIXELS; ++ii) + { + const auto value = static_cast(::rand()) / RAND_MAX * + std::numeric_limits::max(); + retval[ii] = static_cast(value); + } + return retval; +} + +TEST_CASE(testThreadedByteSwap) +{ + constexpr size_t NUM_PIXELS = 10000; + const auto origValues = make_origValues(NUM_PIXELS); + + constexpr size_t numThreads = 4; + + // Byte swap the old-fashioned way + auto values1(origValues); + mt::threadedByteSwap(values1.data(), sizeof(values1[0]), NUM_PIXELS, numThreads); + + // Byte swap into output buffer + std::vector swappedValues2(origValues.size()); + mt::threadedByteSwap(origValues.data(), sizeof(origValues[0]), NUM_PIXELS, numThreads, swappedValues2.data()); + + // Everything should match + for (size_t ii = 0; ii < NUM_PIXELS; ++ii) + { + TEST_ASSERT_EQ(values1[ii], swappedValues2[ii]); + } +} + +TEST_MAIN( + TEST_CHECK(testThreadedByteSwap); + ) + \ No newline at end of file diff --git a/externals/coda-oss/modules/c++/std/include/std/bit b/externals/coda-oss/modules/c++/std/include/std/bit index d564c00202..dbcc0b6c5b 100644 --- a/externals/coda-oss/modules/c++/std/include/std/bit +++ b/externals/coda-oss/modules/c++/std/include/std/bit @@ -28,7 +28,7 @@ // Make it (too?) easy for clients to get our various std:: implementations #ifndef CODA_OSS_NO_std_endian #if CODA_OSS_cpp20 - // Some implementations cliam to be C++20 w/o + // Some implementations claim to be C++20 w/o #if __has_include() // __has_include is C++17 #include #define CODA_OSS_NO_std_endian 1 // provided by implementation, probably C++20 @@ -47,4 +47,22 @@ namespace std // This is slightly uncouth: we're not supposed to augment "std". } #endif // CODA_OSS_NO_std_endian +// Make it (too?) easy for clients to get our various std:: implementations +#ifndef CODA_OSS_NO_std_byteswap + #if CODA_OSS_cpp23 + #include + #define CODA_OSS_NO_std_byteswap 1 // provided by implementation, probably C++23 + #endif + #ifndef CODA_OSS_NO_std_byteswap + #define CODA_OSS_NO_std_byteswap 0 // <= C++20, use our own + #endif +#endif + +#if !CODA_OSS_NO_std_byteswap +namespace std // This is slightly uncouth: we're not supposed to augment "std". +{ + using coda_oss::byteswap; +} +#endif // CODA_OSS_NO_std_byteswap + #endif // CODA_OSS_std_bit_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/std/include/std/span b/externals/coda-oss/modules/c++/std/include/std/span index ffa0d1df97..819befe157 100644 --- a/externals/coda-oss/modules/c++/std/include/std/span +++ b/externals/coda-oss/modules/c++/std/include/std/span @@ -39,6 +39,9 @@ namespace std // This is slightly uncouth: we're not supposed to augment "std". { using coda_oss::span; + + using coda_oss::as_bytes; + using coda_oss::as_writable_bytes; } #endif // CODA_OSS_DEFINE_std_span_ diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AbstractOS.h b/externals/coda-oss/modules/c++/sys/include/sys/AbstractOS.h index 158bce4890..3c8a6b0e68 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/AbstractOS.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/AbstractOS.h @@ -99,6 +99,11 @@ struct CODA_OSS_API AbstractOS const std::string& fragment = "", const std::string& extension = "", bool recursive = true) const; + std::vector search( + const std::vector& searchPaths, + const std::string& fragment = "", + const std::string& extension = "", + bool recursive = true) const; /*! * Does this path exist? diff --git a/externals/coda-oss/modules/c++/sys/include/sys/ByteSwap.h b/externals/coda-oss/modules/c++/sys/include/sys/ByteSwap.h new file mode 100644 index 0000000000..d8145dcced --- /dev/null +++ b/externals/coda-oss/modules/c++/sys/include/sys/ByteSwap.h @@ -0,0 +1,118 @@ +/* ========================================================================= + * This file is part of sys-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * (C) Copyright 2023, Maxar Technologies, Inc. + * + * sys-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ + +#ifndef CODA_OSS_sys_ByteSwap_h_INCLUDED_ +#define CODA_OSS_sys_ByteSwap_h_INCLUDED_ +#pragma once + +#include +#include + +#include +#include + +#include "config/Exports.h" + +#include "ByteSwapValue.h" +#include "Runnable.h" + +namespace sys +{ +/*! + * 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 + */ +coda_oss::span CODA_OSS_API byteSwap(coda_oss::spanbuffer, size_t elemSize); +void CODA_OSS_API byteSwap(void* buffer, size_t elemSize, size_t numElems); + +/*! + * 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 + */ +coda_oss::span CODA_OSS_API byteSwap(coda_oss::span buffer, + size_t elemSize, coda_oss::span outputBuffer); +void CODA_OSS_API byteSwap(const void* buffer, size_t elemSize, size_t numElems, void* outputBuffer); + +struct ByteSwapRunnable final : public sys::Runnable +{ + ByteSwapRunnable(void* buffer, size_t elemSize, size_t startElement, size_t numElements) : + mBuffer(static_cast(buffer) + startElement * elemSize), + mElemSize(elemSize), mNumElements(numElements) + { + } + void run() override + { + byteSwap(mBuffer, mElemSize, mNumElements); + } + + virtual ~ByteSwapRunnable() = default; + ByteSwapRunnable(const ByteSwapRunnable&) = delete; + ByteSwapRunnable& operator=(const ByteSwapRunnable&) = delete; + ByteSwapRunnable(ByteSwapRunnable&&) = delete; + ByteSwapRunnable& operator=(ByteSwapRunnable&&) = delete; + +private: + void* const mBuffer; + const size_t mElemSize; + const size_t mNumElements; +}; + +struct ByteSwapCopyRunnable final : public sys::Runnable +{ + ByteSwapCopyRunnable(const void* buffer, size_t elemSize, size_t startElement, size_t numElements, void* outputBuffer) : + mBuffer(static_cast(buffer) + startElement * elemSize), + mElemSize(elemSize), mNumElements(numElements), + mOutputBuffer(static_cast(outputBuffer) + startElement * elemSize) + { + } + void run() override + { + byteSwap(mBuffer, mElemSize, mNumElements, mOutputBuffer); + } + + virtual ~ByteSwapCopyRunnable() = default; + ByteSwapCopyRunnable(const ByteSwapCopyRunnable&) = delete; + ByteSwapCopyRunnable& operator=(const ByteSwapCopyRunnable&) = delete; + ByteSwapCopyRunnable(ByteSwapCopyRunnable&&) = delete; + ByteSwapCopyRunnable& operator=(ByteSwapCopyRunnable&&) = delete; + +private: + const void* const mBuffer; + const size_t mElemSize; + const size_t mNumElements; + void* const mOutputBuffer; +}; + +} +#endif // CODA_OSS_sys_ByteSwap_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/sys/include/sys/ByteSwapValue.h b/externals/coda-oss/modules/c++/sys/include/sys/ByteSwapValue.h new file mode 100644 index 0000000000..57cffe3f3b --- /dev/null +++ b/externals/coda-oss/modules/c++/sys/include/sys/ByteSwapValue.h @@ -0,0 +1,196 @@ +/* ========================================================================= + * This file is part of sys-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * (C) Copyright 2021, Maxar Technologies, Inc. + * + * sys-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ + +#ifndef CODA_OSS_sys_ByteSwapValue_h_INCLUDED_ +#define CODA_OSS_sys_ByteSwapValue_h_INCLUDED_ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config/Exports.h" + +#include "Span.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace sys +{ + /*! + * Swap bytes for a single value into output buffer. API is `span` rather than `void*` since + * for a single value we know the size. These "low level" routines may be less efficient than + * the templates since it's not possible to specialize on a specific size. + * + * \param buffer to transform + * \param[out] outputBuffer buffer to write swapped elements to + */ + coda_oss::span CODA_OSS_API byteSwap( + coda_oss::span pIn, coda_oss::span outPtr); + + namespace details + { + template + inline auto swapUIntBytes(coda_oss::span inBytes, coda_oss::span outBytes, + std::nothrow_t) noexcept + { + static_assert(std::is_unsigned::value, "TUInt must be 'unsigned'"); + assert(sizeof(TUInt) == inBytes.size()); + assert(inBytes.size() == outBytes.size()); + + const void* const pIn_ = inBytes.data(); + auto const pIn = static_cast(pIn_); + void* const pOut_ = outBytes.data(); + auto const pOut = static_cast(pOut_); + + *pOut = coda_oss::byteswap(*pIn); // at long last, swap the bytes + + // Give the raw byte-swapped bytes back to the caller for easy serialization + return as_bytes(pOut); + } + template + inline auto swapUIntBytes(coda_oss::span inBytes, coda_oss::span outBytes) + { + if (sizeof(TUInt) != inBytes.size()) + { + throw std::invalid_argument("'inBytes.size() != sizeof(TUInt)"); + } + if (inBytes.size() != outBytes.size()) + { + throw std::invalid_argument("'inBytes.size() != outBytes.size()"); + } + return swapUIntBytes(inBytes, outBytes, std::nothrow); + } + + // This is a template so that we can have specializations for different sizes. + // By specializing on `size_t`, a `float` can be "cast" to `uint32_t` (via + // `std::byte`) for byte-swapping. + template + inline auto swapBytes(coda_oss::span inBytes, coda_oss::span outBytes) + { + if (elemSize != inBytes.size()) + { + throw std::invalid_argument("'inBytes.size() != elemSize"); + } + return sys::byteSwap(inBytes, outBytes); // size that wasn't specialized + } + + // avoid copy-paste errors + #define CODA_OSS_define_swapBytes_specialization_(T) template <> inline auto swapBytes \ + (coda_oss::span inBytes, coda_oss::span outBytes) { return swapUIntBytes(inBytes, outBytes); } + CODA_OSS_define_swapBytes_specialization_(uint8_t) // no `;`, it's not needed and generates a -Wpedantic warning + CODA_OSS_define_swapBytes_specialization_(uint16_t) + CODA_OSS_define_swapBytes_specialization_(uint32_t) + CODA_OSS_define_swapBytes_specialization_(uint64_t) + #undef CODA_OSS_define_swapBytes_specialization_ + + template + inline constexpr bool is_byte_swappable() noexcept + { + // Trying to byte-swap anything other than integers is likely to cause + // problems (or at least confusion): + // * `struct`s have padding that should be ignored. + // * each individual member of a `struct` should be byte-swaped + // * byte-swaped `float` or `double` bits are nonsense + return (std::is_integral::value || std::is_enum::value) || !std::is_compound::value; + } + } + + /*! + * Function to swap one element irrespective of size. + * Returns the raw byte-swapped bytes for easy serialization. + */ + template + inline auto swapBytes(coda_oss::span inBytes, coda_oss::span outBytes) + { + static_assert(details::is_byte_swappable(), "T should not be a 'struct'"); + return details::swapBytes(inBytes, outBytes); + } + template + inline auto swapBytes(T in, coda_oss::span outBytes) + { + return swapBytes(as_bytes(in), outBytes); + } + template + inline auto swapBytes(T in) + { + std::vector retval; + retval.resize(sizeof(T)); + std::ignore = swapBytes(in, make_span(retval)); + return retval; + } + + // Reverse the above: turn `span` back to T after byte-swapping + template + inline auto swapBytes(coda_oss::span in) + { + // Don't want to cast the swapped bytes in `in` to T* as they might not be valid; + // e.g., a byte-swapped `float` could be garbage. + T retval; + swapBytes(in, as_writable_bytes(retval)); + return retval; + } + + /*! + * Function to swap one element irrespective of size. The inplace + * buffer function should be preferred. + * + * To specialize complex float, first include the complex library + * \code + #include + * \endcode + * + * Then put an overload in as specified below: + * \code + template std::complex byteSwap(std::complex val) + { + std::complex out(byteSwap(val.real()), + byteSwap(val.imag())); + return out; + } + * \endcode + * + */ + template inline T byteSwap(T val) + { + T out; + std::ignore = swapBytes(val, as_writable_bytes(out)); + return out; + } +} +#endif // CODA_OSS_sys_ByteSwapValue_h_INCLUDED_ 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 28d396c5c3..df36708bb4 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Conf.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Conf.h @@ -66,23 +66,16 @@ #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" +#include "sys/ByteSwap.h" /* Dance around the compiler to figure out */ @@ -205,205 +198,9 @@ namespace sys * RISC architectures we are big-endian. */ bool CODA_OSS_API isBigEndianSystem(); + bool CODA_OSS_API isLittleEndianSystem(); - /*! - * 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 CODA_OSS_API byteSwap_(void* buffer, size_t elemSize, size_t numElems); - template - inline void byteSwap(T* buffer, size_t elemSize, size_t numElems) - { - // 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)) - { - 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 - * must be adjusted accordingly. - * - * \param buffer to transform - * \param elemSize - * \param numElems - * \param[out] outputBuffer buffer to write swapped elements to - */ - 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" - { - // 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)) - { - throw std::invalid_argument("sizeof(T) != 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()) - { - 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); - } - - /*! - * Function to swap one element irrespective of size. The inplace - * buffer function should be preferred. - * - * To specialize complex float, first include the complex library - * \code - #include - * \endcode - * - * Then put an overload in as specified below: - * \code - template std::complex byteSwap(std::complex val) - { - std::complex out(byteSwap(val.real()), - byteSwap(val.imag())); - return out; - } - * \endcode - * - */ - template inline T byteSwap_(T val) - { - // 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); - unsigned char* cIn = reinterpret_cast(&val); - for (size_t i = 0, j = size - 1; i < j; ++i, --j) - { - cOut[i] = cIn[j]; - cOut[j] = cIn[i]; - } - 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 * boundary specified by the user. @@ -459,7 +256,6 @@ namespace sys #endif } - } // https://en.wikipedia.org/wiki/Year_2038_problem diff --git a/externals/coda-oss/modules/c++/sys/include/sys/Span.h b/externals/coda-oss/modules/c++/sys/include/sys/Span.h new file mode 100644 index 0000000000..23b72d02c1 --- /dev/null +++ b/externals/coda-oss/modules/c++/sys/include/sys/Span.h @@ -0,0 +1,188 @@ +/* ========================================================================= + * This file is part of sys-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * (C) Copyright 2023, Maxar Technologies, Inc. + * + * sys-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ + +#ifndef CODA_OSS_sys_Span_h_INCLUDED_ +#define CODA_OSS_sys_Span_h_INCLUDED_ +#pragma once + +#include +#include +#include +#include + +namespace sys // not "mem", it depends on sys. +{ + +// Creating a `span` is verbose w/o deduction guidelines in C++17. +// Some overloads to ease the pain. +template +inline coda_oss::span make_span(const T* ptr, size_t sz) noexcept +{ + return coda_oss::span(ptr, sz); +} +template +inline coda_oss::span make_const_span(T* ptr, size_t sz) noexcept +{ + return coda_oss::span(ptr, sz); +} +template +inline coda_oss::span make_writable_span(T* ptr, size_t sz) noexcept // c.f., as_writable_bytes() +{ + return coda_oss::span(ptr, sz); +} +template +inline coda_oss::span make_span(T* ptr, size_t sz) noexcept +{ + return make_writable_span(ptr, sz); +} + +template +inline auto make_span(const void* ptr, size_t sz) noexcept +{ + return make_span(static_cast(ptr), sz); +} +template +inline auto make_const_span(void* ptr, size_t sz) noexcept +{ + return make_const_span(static_cast(ptr), sz); +} +template +inline auto make_span(void* ptr, size_t sz) noexcept +{ + return make_writable_span(static_cast(ptr), sz); +} + +template +inline auto make_const_span(coda_oss::span v) noexcept // turn span into span +{ + return make_const_span(v.data(), v.size()); +} + +template +inline auto make_span(const std::vector& v) noexcept +{ + return make_span(v.data(), v.size()); +} +template +inline auto make_const_span(std::vector& v) noexcept +{ + return make_const_span(v.data(), v.size()); +} +template +inline auto make_span(std::vector& v) noexcept +{ + return make_writable_span(v.data(), v.size()); +} + +template +inline auto make_span(const std::array& v) noexcept +{ + return make_span(v.data(), v.size()); +} +template +inline auto make_const_span(std::array& v) noexcept +{ + return make_const_span(v.data(), v.size()); +} +template +inline auto make_span(std::array& v) noexcept +{ + return make_writable_span(v.data(), v.size()); +} + +template +inline auto make_span(const T (&a)[N]) noexcept +{ + return make_span(a, N); +} +template +inline auto make_const_span(T (&a)[N]) noexcept +{ + return make_const_span(a, N); +} +template +inline auto make_span(T (&a)[N]) noexcept +{ + return make_writable_span(a, N); +} + +// Calling as_bytes() or as_writable_bytes() requires a span, which as +// noted above is a nuisance to create w/o C++17 +template +inline auto as_bytes(const T* ptr, size_t sz) noexcept +{ + return coda_oss::as_bytes(make_span(ptr, sz)); +} +template +inline auto as_writable_bytes(T* ptr, size_t sz) noexcept +{ + return coda_oss::as_writable_bytes(make_writable_span(ptr, sz)); +} + +template +inline auto as_bytes(const std::vector& v) noexcept +{ + return as_bytes(v.data(), v.size()); +} +template +inline auto as_writable_bytes(std::vector& v) noexcept +{ + return as_writable_bytes(v.data(), v.size()); +} + +template +inline auto as_bytes(const std::array& v) noexcept +{ + return as_bytes(v.data(), v.size()); +} +template +inline auto as_writable_bytes(std::array& v) noexcept +{ + return as_writable_bytes(v.data(), v.size()); +} + +template +inline auto as_bytes(const T (&a)[N]) noexcept +{ + return as_bytes(a, N); +} +template +inline auto as_writable_bytes(T (&a)[N]) noexcept +{ + return as_writable_bytes(a, N); +} + +// "cast" a single value to bytes +template +inline auto as_bytes(const T& v) noexcept +{ + return as_bytes(&v, 1); +} +template +inline auto as_writable_bytes(T& v) noexcept +{ + return as_writable_bytes(&v, 1); +} + +} +#endif // CODA_OSS_sys_Span_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp b/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp index 5494156802..ede2d6cceb 100644 --- a/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp +++ b/externals/coda-oss/modules/c++/sys/source/AbstractOS.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -83,6 +85,31 @@ AbstractOS::search(const std::vector& searchPaths, return elementsFound; } +inline auto convert(const std::vector& paths) +{ + std::vector retval; + std::transform(paths.begin(), paths.end(), std::back_inserter(retval), + [](const fs::path& p) { return p.string(); }); + return retval; +} +inline auto convert(const std::vector& paths) +{ + std::vector retval; + std::transform(paths.begin(), paths.end(), std::back_inserter(retval), + [](const auto& p) { return p; }); + return retval; +} + +std::vector AbstractOS::search( + const std::vector& searchPaths, + const std::string& fragment, + const std::string& extension, + bool recursive) const +{ + const auto results = search(convert(searchPaths), fragment, extension, recursive); + return convert(results); +} + void AbstractOS::remove(const std::string& path) const { if (isDirectory(path)) diff --git a/externals/coda-oss/modules/c++/sys/source/Conf.cpp b/externals/coda-oss/modules/c++/sys/source/Conf.cpp index 414374d5ab..bf29dd69ba 100644 --- a/externals/coda-oss/modules/c++/sys/source/Conf.cpp +++ b/externals/coda-oss/modules/c++/sys/source/Conf.cpp @@ -19,6 +19,7 @@ * see . * */ +#include "sys/Conf.h" #include @@ -26,49 +27,100 @@ #include #include #include - -#include "sys/Conf.h" #include "coda_oss/bit.h" +#include "coda_oss/cstddef.h" +#include "coda_oss/span.h" -using endian = coda_oss::endian; +#include "sys/Span.h" // https://en.cppreference.com/w/cpp/types/endian -inline bool is_big_endian() +using endian = coda_oss::endian; +template +inline bool is_big_endian_() +{ + throw std::logic_error("Mixed-endian not supported."); +} +template<> +inline constexpr bool is_big_endian_() +{ + return true; +} +template <> +inline constexpr bool is_big_endian_() +{ + return false; +} +constexpr inline bool is_big_endian() +{ + return is_big_endian_(); +} + +// Want to explicitly test against both endian::bit and endian::little; i.e., +// because of "mixed" endianness, little may not the same as !big +template +inline bool is_little_endian_() { - auto endianness = endian::native; // "conditional expression is constant" - if (endianness == endian::big) - { - return true; - } - if (endianness == endian::little) - { - return false; - } throw std::logic_error("Mixed-endian not supported."); } +template <> +inline constexpr bool is_little_endian_() +{ + return false; +} +template <> +inline constexpr bool is_little_endian_() +{ + return true; +} +constexpr inline bool is_little_endian() +{ + return is_little_endian_(); +} + constexpr inline bool is_big_or_little_endian() { - return (endian::native == endian::big) || (endian::native == endian::little) ? true : false; + return is_big_endian() || is_little_endian(); } -inline bool isBigEndianSystem() +inline bool testIsBigEndianSystem() { // This is an endian test int intVal = 1; unsigned char* endianTest = (unsigned char*)&intVal; return endianTest[0] != 1; } -bool sys::isBigEndianSystem() +inline auto isBigEndianSystem_() { static_assert(is_big_or_little_endian(), "Mixed-endian not supported."); - const auto retval = ::isBigEndianSystem(); + const auto retval = testIsBigEndianSystem(); if (retval != is_big_endian()) { throw std::logic_error("endian values don't agree!"); } return retval; } - +inline auto isLittleEndianSystem_() +{ + static_assert(is_big_or_little_endian(), "Mixed-endian not supported."); + const auto retval = !testIsBigEndianSystem(); + if (retval != is_little_endian()) + { + throw std::logic_error("endian values don't agree!"); + } + return retval; +} +bool sys::isBigEndianSystem() +{ + auto const retval = isBigEndianSystem_(); + assert(!retval == isLittleEndianSystem_()); + return retval; +} +bool sys::isLittleEndianSystem() +{ + auto const retval = isLittleEndianSystem_(); + assert(!retval == isBigEndianSystem_()); + return retval; +} /*! * Swap bytes in-place. Note that a complex pixel @@ -79,9 +131,67 @@ bool sys::isBigEndianSystem() * \param elemSize * \param numElems */ -void sys::byteSwap_(void* buffer, size_t elemSize, size_t numElems) +template +inline void byteSwap_n_(void *buffer_, size_t numElems) { - byteSwap_(buffer, elemSize, numElems, buffer); + static_assert(std::is_unsigned::value, "TUInt must be 'unsigned'"); + const auto buffer = sys::make_span(buffer_, numElems); + assert(buffer.size_bytes() == sizeof(TUInt) * numElems); + + for (auto& v : buffer) + { + v = sys::byteSwap(v); + } +} +template +inline void byteSwap_n(void *buffer, size_t elemSize, size_t numElems) +{ + if (sizeof(TUInt) != elemSize) + { + throw std::invalid_argument("'elemSize' != sizeof(TUInt)"); + } + byteSwap_n_(buffer, numElems); +} +void sys::byteSwap(void* buffer, size_t elemSize, size_t numElems) +{ + if ((buffer == nullptr) || (elemSize < 2) || (numElems == 0)) + return; + + switch (elemSize) + { + case sizeof(uint16_t): return byteSwap_n(buffer, elemSize, numElems); + case sizeof(uint32_t): return byteSwap_n(buffer, elemSize, numElems); + case sizeof(uint64_t): return byteSwap_n(buffer, elemSize, numElems); + default: break; + } + + auto const bufferPtr = static_cast(buffer); + const auto half = elemSize >> 1; + size_t offset = 0, innerOff = 0, innerSwap = 0; + for (size_t i = 0; i < numElems; ++i, offset += elemSize) + { + for (size_t j = 0; j < half; ++j) + { + innerOff = offset + j; + innerSwap = offset + elemSize - 1 - j; + + std::swap(bufferPtr[innerOff], bufferPtr[innerSwap]); + } + } +} +coda_oss::span sys::byteSwap(coda_oss::span buffer, size_t elemSize) +{ + if ((buffer.size() == 0) || (elemSize < 2)) + return sys::make_const_span(buffer); + + size_t const numElems = buffer.size() / elemSize; + if ((numElems * elemSize) != buffer.size()) + { + throw std::invalid_argument("'buffer' is not a multiple of 'elemSize'"); + } + + byteSwap(buffer.data(), elemSize, numElems); + return sys::make_const_span(buffer); } /*! @@ -95,61 +205,106 @@ void sys::byteSwap_(void* buffer, size_t elemSize, size_t 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_) +inline void byteSwap_n_(const void *buffer_, 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); + const auto buffer = sys::make_span(buffer_, numElems); + assert(buffer.size_bytes() == sizeof(TUInt) * numElems); + const auto outputBuffer = sys::make_span(outputBuffer_, numElems); - std::transform(buffer.begin(), buffer.end(), outputBuffer.begin(), [](const auto& v) { return sys::byteSwap(v); }); + const auto byteSwap = [](const auto& v) { return sys::byteSwap(v); }; + std::transform(buffer.begin(), buffer.end(), outputBuffer.begin(), byteSwap); } - -void sys::byteSwap_(const void* buffer, - size_t elemSize, - size_t numElems, - void* outputBuffer) +template +inline void byteSwap_n(const void *buffer, size_t elemSize, size_t numElems, void *outputBuffer) { - if (!numElems || !buffer || !outputBuffer) - { - return; - } - - if (elemSize == 2) + if (sizeof(TUInt) != elemSize) { - return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + throw std::invalid_argument("'elemSize' != sizeof(TUInt)"); } - if (elemSize == 4) + byteSwap_n_(buffer, numElems, outputBuffer); +} +void sys::byteSwap(const void* buffer, size_t elemSize, size_t numElems, void* outputBuffer) +{ + if ((numElems == 0) || (buffer == nullptr) || (outputBuffer == nullptr)) { - return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + return; } - if (elemSize == 8) + + switch (elemSize) { - return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + case 1: std::ignore = memcpy(outputBuffer, buffer, elemSize * numElems); return; + case 2: return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + case 4: return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + case 8: return byteSwap_n(buffer, elemSize, numElems, outputBuffer); + default: break; } - const sys::byte* bufferPtr = static_cast(buffer); - sys::byte* outputBufferPtr = static_cast(outputBuffer); + auto const bufferPtr = static_cast(buffer); + auto const 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) + for (size_t 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; + outputBufferPtr[innerOff] = bufferPtr[innerSwap]; + outputBufferPtr[innerSwap] = bufferPtr[innerOff]; } } } +coda_oss::span sys::byteSwap(coda_oss::span buffer, + size_t elemSize, coda_oss::span outputBuffer) +{ + if ((buffer.size() == 0) || (outputBuffer.size() == 0)) + { + return sys::make_const_span(outputBuffer); + } + + size_t const numElems = buffer.size() / elemSize; + if ((numElems * elemSize) != buffer.size()) + { + throw std::invalid_argument("'buffer' is not a multiple of 'elemSize'"); + } + if (buffer.size() != outputBuffer.size()) + { + throw std::invalid_argument("'buffer' and 'outputBuffer' are different sizes'"); + } + + byteSwap(buffer.data(), elemSize, numElems, outputBuffer.data()); + return sys::make_const_span(outputBuffer); +} + + coda_oss::span sys::byteSwap( + coda_oss::span inPtr, + coda_oss::span outPtr) +{ + if (inPtr.size() != outPtr.size()) + { + throw std::invalid_argument("'size of byte buffers must match"); + } + + const auto elemSize = inPtr.size(); + switch (elemSize) + { + case sizeof(uint8_t): return details::swapUIntBytes(inPtr, outPtr, std::nothrow); + case sizeof(uint16_t): return details::swapUIntBytes(inPtr, outPtr, std::nothrow); + case sizeof(uint32_t): return details::swapUIntBytes(inPtr, outPtr, std::nothrow); + case sizeof(uint64_t): return details::swapUIntBytes(inPtr, outPtr, std::nothrow); + default: break; + } + + for (size_t ii = 0, jj = elemSize - 1; ii < jj; ++ii, --jj) + { + outPtr[ii] = inPtr[jj]; + outPtr[jj] = inPtr[ii]; + } + + // Give the raw byte-swapped bytes back to the caller for easy serialization + return make_const_span(outPtr); +} 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 6ef347cb3f..954872bb0b 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,14 +22,14 @@ #include "TestCase.h" -#include - #include +#include #include // std::endian -#include // std::byte +#include #include #include +#include TEST_CASE(testEndianness) { @@ -72,29 +72,32 @@ TEST_CASE(testEndianness) } } -TEST_CASE(testByteSwap) +static std::vector make_origValues(size_t NUM_PIXELS) { ::srand(334); - static const size_t NUM_PIXELS = 10000; - std::vector origValues(NUM_PIXELS); + std::vector retval(NUM_PIXELS); for (size_t ii = 0; ii < NUM_PIXELS; ++ii) { const auto value = static_cast(::rand()) / RAND_MAX * - std::numeric_limits::max(); - origValues[ii] = static_cast(value); + std::numeric_limits::max(); + retval[ii] = static_cast(value); } + return retval; +} - // Byte swap the old-fashioned way - std::vector values1(origValues); - sys::byteSwap(&values1[0], sizeof(sys::Uint64_T), NUM_PIXELS); +TEST_CASE(testByteSwap) +{ + constexpr size_t NUM_PIXELS = 10000; + const auto origValues = make_origValues(NUM_PIXELS); + // Byte swap the old-fashioned way + auto values1(origValues); + sys::byteSwap(values1.data(), sizeof(uint64_t), NUM_PIXELS); // Byte swap into output buffer - const std::vector values2(origValues); - std::vector swappedValues2(values2.size()); - sys::byteSwap(&values2[0], sizeof(sys::Uint64_T), NUM_PIXELS, - &swappedValues2[0]); + std::vector swappedValues2(origValues.size()); + sys::byteSwap(origValues.data(), sizeof(uint64_t), NUM_PIXELS, swappedValues2.data()); // Everything should match for (size_t ii = 0; ii < NUM_PIXELS; ++ii) @@ -103,6 +106,7 @@ TEST_CASE(testByteSwap) } } +// 0xnn is an `int` which can't be used to initialize std::byte w/o a cast #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); @@ -127,263 +131,133 @@ 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) +template +static void testByteSwapValues_(const std::string& testName, const void* pBytes) { - 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); - } + auto pUInt = static_cast(pBytes); + auto swap = sys::byteSwap(*pUInt); + TEST_ASSERT_NOT_EQ(*pUInt, 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++) + auto const pResultBytes = static_cast(pResult_); + auto const pValueBytes = static_cast(pBytes); + for (size_t i = 0, j = sizeof(TUInt); i < sizeof(TUInt) && j > 0; i++, j--) { - TEST_ASSERT(pResultBytes[i] == sixteen_bytes[i]); + TEST_ASSERT(pResultBytes[i] == pValueBytes[j-1]); } -} -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); - } + TEST_ASSERT_EQ(*pUInt, 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]); - } -} + // swap as an "array" of one value + sys::byteSwap(pUInt, sizeof(TUInt), 1, &swap); + TEST_ASSERT_NOT_EQ(*pUInt, swap); + sys::byteSwap(&swap, sizeof(TUInt), 1); // swap back + TEST_ASSERT_EQ(*pUInt, swap); -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 0; i++, j--) { - // can't test swapped bytes against anything; might not be a valid `float` - swap = sys::byteSwap(outputBuffer[i]); - TEST_ASSERT_EQ(values[i], swap); - } - - // in-place swap - sys::byteSwap(outputBuffer.data(), sizeof(float), outputBuffer.size()); - for (size_t i = 0; i < outputBuffer.size(); i++) - { - TEST_ASSERT_EQ(values[i], outputBuffer[i]); + TEST_ASSERT(resultBytes[i] == pValueBytes[j - 1]); } } - -TEST_CASE(testByteSwapDouble) +TEST_CASE(testByteSwapValues) { - using value_type = double; - static_assert(sizeof(value_type) == sizeof(uint64_t), "sizeof(value_type) != sizeof(uint64_t)"); - - const value_type v = 3.141592654; - const void* pVoid = &v; - auto pDouble = static_cast(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]); - } + testByteSwapValues_(testName, two_bytes); + testByteSwapValues_(testName, four_bytes); + testByteSwapValues_(testName, eight_bytes); } -TEST_CASE(testByteSwapCxFloat) +TEST_CASE(testByteSwap12) { - 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 a goofy element size + static constexpr std::byte twelve_bytes[]{ + x00, x11, x22, x33, x44, x55, + x99, xAA, xBB, xDD, xEE, xFF}; + const auto pValueBytes = sys::as_bytes(twelve_bytes); + + std::vector swappedValues(12); + auto pResultBytes = sys::make_span(swappedValues); + + auto elemSize = 12; + auto numElements = swappedValues.size() / elemSize; + sys::byteSwap(twelve_bytes, elemSize, numElements, pResultBytes.data()); + TEST_ASSERT(pResultBytes[0] == pValueBytes[11]); + TEST_ASSERT(pResultBytes[1] == pValueBytes[10]); + TEST_ASSERT(pResultBytes[2] == pValueBytes[9]); + TEST_ASSERT(pResultBytes[3] == pValueBytes[8]); 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(pValueBytes, pResultBytes); + TEST_ASSERT(result[0] == pValueBytes[11]); + TEST_ASSERT(result[1] == pValueBytes[10]); + TEST_ASSERT(result[2] == pValueBytes[9]); + TEST_ASSERT(result[3] == pValueBytes[8]); + TEST_ASSERT(result[4] == pValueBytes[7]); + TEST_ASSERT(result[5] == pValueBytes[6]); + TEST_ASSERT(result[6] == pValueBytes[5]); + TEST_ASSERT(result[7] == pValueBytes[4]); + TEST_ASSERT(result[8] == pValueBytes[3]); + TEST_ASSERT(result[9] == pValueBytes[2]); + TEST_ASSERT(result[10] == pValueBytes[1]); + TEST_ASSERT(result[11] == pValueBytes[0]); + + + elemSize = 6; // note that an ODD size doesn't work correctly + numElements = swappedValues.size() / elemSize; + sys::byteSwap(twelve_bytes, elemSize, numElements, swappedValues.data()); + TEST_ASSERT(pResultBytes[0] == pValueBytes[5]); + TEST_ASSERT(pResultBytes[1] == pValueBytes[4]); + TEST_ASSERT(pResultBytes[2] == pValueBytes[3]); + TEST_ASSERT(pResultBytes[3] == pValueBytes[2]); + TEST_ASSERT(pResultBytes[4] == pValueBytes[1]); + TEST_ASSERT(pResultBytes[5] == pValueBytes[0]); + + TEST_ASSERT(pResultBytes[6] == pValueBytes[11]); + TEST_ASSERT(pResultBytes[7] == pValueBytes[10]); + TEST_ASSERT(pResultBytes[8] == pValueBytes[9]); + TEST_ASSERT(pResultBytes[9] == pValueBytes[8]); + TEST_ASSERT(pResultBytes[10] == pValueBytes[7]); + TEST_ASSERT(pResultBytes[11] == pValueBytes[6]); + + sys::byteSwap(swappedValues.data(), elemSize, numElements); // swap back + for (size_t i = 0; i < swappedValues.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); + TEST_ASSERT(pResultBytes[i] == pValueBytes[i]); } +} - // in-place swap - sys::byteSwap(outputBuffer_); - for (size_t i = 0; i < outputBuffer_.size(); i++) - { - TEST_ASSERT_EQ(values[i], outputBuffer_[i]); - } +template +static inline void six_byteSwap(const void* in, T& out) +{ + auto const inBytes = sys::make_span(in, sizeof(T)); + out = sys::swapBytes(inBytes); +} +TEST_CASE(testSixByteSwap) +{ + const int i = 123; + int i_swapped; + six_byteSwap(&i, i_swapped); + TEST_ASSERT_NOT_EQ(i, i_swapped); + + int result; + six_byteSwap(&i_swapped, result); + TEST_ASSERT_EQ(i, result); } TEST_MAIN( TEST_CHECK(testEndianness); TEST_CHECK(testByteSwap); - TEST_CHECK(testByteSwapUInt16); - TEST_CHECK(testByteSwapUInt32); - TEST_CHECK(testByteSwapUInt64); - TEST_CHECK(testByteSwapFloat); - TEST_CHECK(testByteSwapDouble); - TEST_CHECK(testByteSwapCxFloat); + TEST_CHECK(testByteSwapValues); + TEST_CHECK(testByteSwap12); + TEST_CHECK(testSixByteSwap); ) - \ No newline at end of file diff --git a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/Attributes.h b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/Attributes.h index 8b354844bc..b8823164d4 100644 --- a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/Attributes.h +++ b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/Attributes.h @@ -217,6 +217,10 @@ struct Attributes final { return static_cast(size()); } + bool empty() const + { + return mAttributes.empty(); + } /*! * Look up an attribute's local name by index. @@ -396,6 +400,16 @@ struct Attributes final { mAttributes.clear(); } + + auto begin() const + { + return mAttributes.begin(); + } + auto end() const + { + return mAttributes.end(); + } + private: //! Underlying representation Attributes_T mAttributes; diff --git a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorXerces.h b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorXerces.h index 8942aaf57f..3cdde68844 100644 --- a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorXerces.h +++ b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ValidatorXerces.h @@ -137,6 +137,9 @@ class ValidatorXerces : public ValidatorInterface bool validate(const coda_oss::u8string&, const std::string& xmlID, std::vector&) const override; bool validate(const str::W1252string&, const std::string& xmlID, std::vector&) const override; + // Search each directory for XSD files + static std::vector loadSchemas(const std::vector& schemaPaths, bool recursive=true); + private: bool validate_(const coda_oss::u8string& xml, const std::string& xmlID, diff --git a/externals/coda-oss/modules/c++/xml.lite/source/ValidatorXerces.cpp b/externals/coda-oss/modules/c++/xml.lite/source/ValidatorXerces.cpp index 7657e7b8a8..7e794d4e4b 100644 --- a/externals/coda-oss/modules/c++/xml.lite/source/ValidatorXerces.cpp +++ b/externals/coda-oss/modules/c++/xml.lite/source/ValidatorXerces.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // std::ignore #include #include @@ -89,6 +90,14 @@ inline std::vector convert(const std::vector& schemaPaths [](const fs::path& p) { return p.string(); }); return retval; } +inline auto convert(const std::vector& paths) +{ + std::vector retval; + std::transform(paths.begin(), paths.end(), std::back_inserter(retval), + [](const auto& p) { return p; }); + return retval; +} + ValidatorXerces::ValidatorXerces( const std::vector& schemaPaths, logging::Logger* log, @@ -154,19 +163,18 @@ ValidatorXerces::ValidatorXerces( // load our schemas -- // search each directory for schemas - sys::OS os; - std::vector schemas = - os.search(schemaPaths, "", ".xsd", recursive); + const auto schemas = loadSchemas(convert(schemaPaths), recursive); // add the schema to the validator - for (size_t i = 0; i < schemas.size(); ++i) + // add the schema to the validator + for (auto&& schema : schemas) { - if (!mValidator->loadGrammar(schemas[i].c_str(), + if (!mValidator->loadGrammar(schema.c_str(), xercesc::Grammar::SchemaGrammarType, true)) { std::ostringstream oss; - oss << "Error: Failure to load schema " << schemas[i]; + oss << "Error: Failure to load schema " << schema; log->warn(Ctxt(oss.str())); } } @@ -175,6 +183,14 @@ ValidatorXerces::ValidatorXerces( mSchemaPool->lockPool(); } +std::vector ValidatorXerces::loadSchemas(const std::vector& schemaPaths, bool recursive) +{ + // load our schemas -- + // search each directory for schemas + sys::OS os; + return os.search(schemaPaths, "", ".xsd", recursive); +} + // From config.h.in: Define to the 16 bit type used to represent Xerces UTF-16 characters // On Windows, this needs to be wchar_t so that various "wide character" Win32 APIs can be called. static_assert(sizeof(XMLCh) == 2, "XMLCh should be two bytes for UTF-16."); 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 f4b4930c02..16000dd616 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::byteSwapV(arg1, arg2, arg3); + sys::byteSwap(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::byteSwapV((void const *)arg1,arg2,arg3,arg4); + sys::byteSwap((void const *)arg1,arg2,arg3,arg4); resultobj = SWIG_Py_Void(); return resultobj; fail: diff --git a/modules/c++/nitf/include/nitf/DecompressionInterface.hpp b/modules/c++/nitf/include/nitf/DecompressionInterface.hpp index 55e815b046..14a75889a7 100644 --- a/modules/c++/nitf/include/nitf/DecompressionInterface.hpp +++ b/modules/c++/nitf/include/nitf/DecompressionInterface.hpp @@ -24,6 +24,8 @@ #define __NITF_DECOMPRESSION_INTERFACE_HPP__ #pragma once +#include + #include #include #include @@ -87,10 +89,8 @@ namespace nitf * during decompression, and gives a c++ api for getting * things done. */ -class DecompressionInterface +struct DecompressionInterface final { -public: - //! These are canned methods which turn around // and call the nitf_DecompressionControl of your choice static NITF_BOOL adapterStart(nitf_DecompressionControl* object, @@ -121,7 +121,7 @@ class DecompressionInterface * \class Compressor * \brief This is the c++ interface for nitf_CompressionControl */ -struct Decompressor +struct Decompressor final { Decompressor() = default; virtual ~Decompressor() {} diff --git a/modules/c++/nitf/source/DecompressionInterface.cpp b/modules/c++/nitf/source/DecompressionInterface.cpp index d91bebe962..06328eef48 100644 --- a/modules/c++/nitf/source/DecompressionInterface.cpp +++ b/modules/c++/nitf/source/DecompressionInterface.cpp @@ -26,6 +26,35 @@ using namespace nitf; +template +static inline auto invoke(nitf_Error* error, const char* file, int line, const char* func, + TReturn catch_return, TFunction f) +{ + try + { + return f(); + } + catch (const except::Exception& ex) + { + Utils::error_init(error, ex.getMessage(), file, line, func, NRT_ERR_DECOMPRESSION); + } + catch (const std::exception& ex) + { + Utils::error_init(error, ex, file, line, func, NRT_ERR_DECOMPRESSION); + } + catch (...) + { + nrt_Error_init(error, "Unknown error", file, line, func, NRT_ERR_DECOMPRESSION); + } + + return catch_return; +} + +// Ensure the return type is deduced correctly. NRT_SUCCESS/NRT_FAILURE +// is `int`, not `NITF_BOOL`. +constexpr NITF_BOOL nrt_success = NRT_SUCCESS; +constexpr NITF_BOOL nrt_failure = NRT_FAILURE; + NITF_BOOL DecompressionInterface::adapterStart( nitf_DecompressionControl* object, nitf_IOInterface* io, @@ -35,7 +64,7 @@ NITF_BOOL DecompressionInterface::adapterStart( uint64_t* blockMask, nitf_Error* error) { - try + return invoke(error, NRT_CTXT, nrt_failure, [&]() { nitf::IOInterface ioInter(io); ioInter.setManaged(true); @@ -46,26 +75,8 @@ NITF_BOOL DecompressionInterface::adapterStart( fileLength, blockInfo, blockMask); - return NRT_SUCCESS; - } - catch (const except::Exception& ex) - { - Utils::error_init(error, ex.getMessage(), NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return NRT_FAILURE; - } - catch (const std::exception& ex) - { - Utils::error_init(error, ex, NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return NRT_FAILURE; - } - catch (...) - { - nrt_Error_init(error, "Unknown error", NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return NRT_FAILURE; - } + return nrt_success; + }); } uint8_t* DecompressionInterface::adapterReadBlock( @@ -74,29 +85,24 @@ uint8_t* DecompressionInterface::adapterReadBlock( uint64_t* blockSize, nitf_Error* error) { - try - { + const auto f = [&]() { return static_cast(object)->readBlock(blockNumber, blockSize); - } - catch (const except::Exception& ex) - { - Utils::error_init(error, ex.getMessage(), NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return nullptr; - } - catch (const std::exception& ex) - { - Utils::error_init(error, ex, NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return nullptr; - } - catch (...) + }; + using retval_t = decltype(f()); + return invoke(error, NRT_CTXT, static_cast(nullptr), f); +} + +NITF_BOOL DecompressionInterface::adapterFreeBlock( + nitf_DecompressionControl* object, + uint8_t* block, + nitf_Error* error) +{ + return invoke(error, NRT_CTXT, nrt_failure, [&]() { - nrt_Error_init(error, "Unknown error", NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return nullptr; - } + static_cast(object)->freeBlock(block); + return nrt_success; + }); } NITF_BOOL DecompressionInterface::adapterFreeBlock( @@ -104,29 +110,11 @@ NITF_BOOL DecompressionInterface::adapterFreeBlock( std::byte* block, nitf_Error* error) { - try + return invoke(error, NRT_CTXT, nrt_failure, [&]() { static_cast(object)->freeBlock(block); - return NRT_SUCCESS; - } - catch (const except::Exception& ex) - { - Utils::error_init(error, ex.getMessage(), NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return NRT_FAILURE; - } - catch (const std::exception& ex) - { - Utils::error_init(error, ex, NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return NRT_FAILURE; - } - catch (...) - { - nrt_Error_init(error, "Unknown error", NRT_CTXT, - NRT_ERR_DECOMPRESSION); - return NRT_FAILURE; - } + return nrt_success; + }); } void DecompressionInterface::adapterDestroy(