From 34a50a6daa874de462a5b0717d313687a298c70d Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Mon, 2 Oct 2023 13:17:09 -0400 Subject: [PATCH] Squashed 'externals/nitro/' changes from 732538e80..ff335eeaf ff335eeaf Merge commit 'eab6b6c35439c1eb7fa22fb042aae4a61a936d66' into cpp17 eab6b6c35 Squashed 'externals/coda-oss/' changes from 14f0b1545c..70a006d8a4 5648a0267 Merge branch 'main' into cpp17 b26e15318 latest from CODA-OSS (#583) 0db9bdb29 fix ASAN diagnostics (#582) git-subtree-dir: externals/nitro git-subtree-split: ff335eeaf8071f45a0e15cbba70ffdf7bcb8a15f --- .../.github/workflows/build_unittest.yml | 10 ++-- externals/coda-oss/CMakeLists.txt | 11 ---- externals/coda-oss/cmake/CodaBuild.cmake | 37 ++++++++++++++ .../modules/c++/avx/unittests/test_m256.cpp | 44 ++++++++++++++++ .../modules/c++/cli/source/ArgumentParser.cpp | 4 +- .../modules/c++/cli/unittests/test_cli.cpp | 37 ++++++++++---- .../c++/coda_oss/include/coda_oss/span.h | 28 +++++++++++ .../c++/hdf5.lite/unittests/test_highfive.cpp | 2 +- .../modules/c++/io/include/io/ByteStream.h | 9 ++-- .../modules/c++/io/source/ByteStream.cpp | 12 +++-- .../io/source/RotatingFileOutputStream.cpp | 7 +-- .../modules/c++/io/unittests/test_streams.cpp | 4 +- .../unittests/test_exception_logger.cpp | 3 +- .../c++/math.linear/unittests/test_Vector.cpp | 11 ++-- .../include/mt/CPUAffinityInitializerLinux.h | 1 + .../mt/source/CPUAffinityInitializerLinux.cpp | 8 +-- .../modules/c++/re/source/RegexPCRE.cpp | 15 +++++- .../include/sio/lite/UserDataDictionary.h | 1 + .../c++/sio.lite/source/FileHeader.cpp | 6 +-- .../coda-oss/modules/c++/std/include/std/span | 47 ++++++++++++++++++ .../modules/c++/str/unittests/test_str.cpp | 5 +- .../c++/sys/unittests/test_NaN_testing.cpp | 36 +++++++++++++- .../xml.lite/unittests/test_xmlelement.cpp | 9 ++-- .../c++/xml.lite/unittests/test_xmlparser.cpp | 7 +-- .../modules/c++/zip/include/zip/ZipFile.h | 6 ++- .../unittests/tests_high_five_base.cpp | 5 ++ .../modules/drivers/uuid/CMakeLists.txt | 2 +- .../modules/drivers/uuid/e2fsprogs-1.47.0.tar | Bin 7956480 -> 7956480 bytes modules/c/nrt/unittests/test_buffer_adapter.c | 10 +++- 29 files changed, 308 insertions(+), 69 deletions(-) diff --git a/externals/coda-oss/.github/workflows/build_unittest.yml b/externals/coda-oss/.github/workflows/build_unittest.yml index 2154ff7b8a..07f04cf859 100644 --- a/externals/coda-oss/.github/workflows/build_unittest.yml +++ b/externals/coda-oss/.github/workflows/build_unittest.yml @@ -58,7 +58,8 @@ jobs: os: [windows-latest] platform: [x64] configuration: [Debug] # Debug turns on more compiler warnings - name: ${{ matrix.os }}-msbuild + avx: [AVX2, AVX512F] + name: ${{ matrix.os }}-${{ matrix.avx }}-msbuild runs-on: ${{ matrix.os }} steps: @@ -68,7 +69,7 @@ jobs: ls env: mkdir out cd out - cmake .. -DCMAKE_INSTALL_PREFIX=install\${{ matrix.platform }}-${{ matrix.configuration }} -DENABLE_PYTHON=OFF + cmake .. -DCMAKE_INSTALL_PREFIX=install\${{ matrix.platform }}-${{ matrix.configuration }} -DENABLE_PYTHON=OFF -DENABLE_${{ matrix.avx }}=ON - name: build run: | cd out @@ -134,14 +135,15 @@ jobs: matrix: os: [ubuntu-latest] configuration: [Debug, Release] - name: ${{ matrix.os }}-${{ matrix.configuration }}-CMake + avx: [AVX2, AVX512F] + name: ${{ matrix.os }}-${{ matrix.configuration }}-${{ matrix.avx }}-CMake runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - name: configure run: | mkdir out && cd out - cmake .. -DENABLE_PYTHON=OFF + cmake .. -DENABLE_PYTHON=OFF -DENABLE_ASAN=ON -DENABLE_${{ matrix.avx }}=ON - name: build run: | cd out diff --git a/externals/coda-oss/CMakeLists.txt b/externals/coda-oss/CMakeLists.txt index 6ef7c1b353..b063bbe618 100644 --- a/externals/coda-oss/CMakeLists.txt +++ b/externals/coda-oss/CMakeLists.txt @@ -24,19 +24,8 @@ if (${CMAKE_PROJECT_NAME} STREQUAL coda-oss) if (MSVC) add_compile_options(/WX) # warnings as errors add_compile_options(/MP) # multi-processor compile - - if (ENABLE_ASAN) - # https://docs.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-160 - add_compile_options(/fsanitize=address) - endif() elseif (UNIX) add_compile_options(-Werror) # warnings as errors - - if (ENABLE_ASAN) - # https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html - add_compile_options(-fsanitize=address) - add_link_options(-fsanitize=address) - endif() endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") diff --git a/externals/coda-oss/cmake/CodaBuild.cmake b/externals/coda-oss/cmake/CodaBuild.cmake index 69a143c1b2..1427737888 100644 --- a/externals/coda-oss/cmake/CodaBuild.cmake +++ b/externals/coda-oss/cmake/CodaBuild.cmake @@ -165,6 +165,22 @@ macro(coda_initialize_build) # This should probably be replaced by GenerateExportHeader #set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD TRUE) + + if (ENABLE_ASAN) + # https://docs.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-160 + add_compile_options(/fsanitize=address) + endif() + + # Note SSE2 is implicitly enabled for x64 builds. + if (ENABLE_AVX2 AND (NOT ENABLE_AVX512F)) + # https://learn.microsoft.com/en-us/cpp/build/reference/arch-x86?view=msvc-170 + add_compile_options(/arch:AVX2) + endif() + if (ENABLE_AVX512F) + # https://learn.microsoft.com/en-us/cpp/build/reference/arch-x86?view=msvc-170 + add_compile_options(/arch:AVX512) + endif() + endif() # Unix/Linux specific options @@ -173,6 +189,27 @@ macro(coda_initialize_build) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 ) + + if (ENABLE_ASAN) + # https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) + endif() + + # Note SSE2 is implicitly enabled for x64 builds. + if (ENABLE_AVX2 AND (NOT ENABLE_AVX512F)) + # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html + # It doesn't look like GCC has a specific option for AVX2; + # other projects use "haswell" + add_compile_options(-march=haswell) + endif() + if (ENABLE_AVX512F) + # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html + # It doesn't look like GCC has a specific option for AVX512F; + # other projects use "native" which isn't quite correct.' + add_compile_options(-march=native) + endif() + endif() # all targets should be installed using this export set diff --git a/externals/coda-oss/modules/c++/avx/unittests/test_m256.cpp b/externals/coda-oss/modules/c++/avx/unittests/test_m256.cpp index 8c6cdc47b9..920a0f9f8d 100644 --- a/externals/coda-oss/modules/c++/avx/unittests/test_m256.cpp +++ b/externals/coda-oss/modules/c++/avx/unittests/test_m256.cpp @@ -25,7 +25,9 @@ #include #include +#include #include +#include TEST_CASE(extractf) { @@ -49,8 +51,50 @@ TEST_CASE(extractf) */ TEST_SUCCESS; } + +TEST_CASE(test_getSIMDInstructionSet) +{ + // This is the reverse of getSIMDInstructionSet(): it uses the macros to generate a value. + constexpr auto simdInstructionSet = sys::getSIMDInstructionSet(); + #if __AVX512F__ + static_assert(simdInstructionSet == sys::SIMDInstructionSet::AVX512F, "getSIMDInstructionSet()"); + #elif __AVX2__ + static_assert(simdInstructionSet == sys::SIMDInstructionSet::AVX2, "getSIMDInstructionSet()"); + #else + static_assert(simdInstructionSet == sys::SIMDInstructionSet::SSE2, "getSIMDInstructionSet()"); + #endif + CODA_OSS_disable_warning_push + #if _MSC_VER + #pragma warning(disable: 4127) // conditional expression is constant + #endif + + switch (sys::getSIMDInstructionSet()) // run-time value + { + case sys::SIMDInstructionSet::SSE2: + { + TEST_ASSERT(simdInstructionSet == sys::SIMDInstructionSet::SSE2); + break; + } + case sys::SIMDInstructionSet::AVX2: + { + TEST_ASSERT(simdInstructionSet == sys::SIMDInstructionSet::AVX2); + break; + } + case sys::SIMDInstructionSet::AVX512F: + { + TEST_ASSERT(simdInstructionSet == sys::SIMDInstructionSet::AVX512F); + break; + } + default: + { + TEST_FAIL; + } + } + CODA_OSS_disable_warning_pop +} TEST_MAIN( TEST_CHECK(extractf); + TEST_CHECK(test_getSIMDInstructionSet); ) diff --git a/externals/coda-oss/modules/c++/cli/source/ArgumentParser.cpp b/externals/coda-oss/modules/c++/cli/source/ArgumentParser.cpp index 3694e7ed31..e7763e4aae 100644 --- a/externals/coda-oss/modules/c++/cli/source/ArgumentParser.cpp +++ b/externals/coda-oss/modules/c++/cli/source/ArgumentParser.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -533,8 +534,7 @@ std::unique_ptr cli::ArgumentParser::parse(const std::string& prog break; } } - if (maxArgs >= 0 && - v->size() >= static_cast(maxArgs)) + if (maxArgs >= 0 && std::ssize(*v) >= maxArgs) { // it's another positional argument, so we break out break; diff --git a/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp b/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp index 62fa2192f9..ade63b34ac 100644 --- a/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp +++ b/externals/coda-oss/modules/c++/cli/unittests/test_cli.cpp @@ -20,12 +20,16 @@ * */ +#include + +#include +#include +#include + #include #include + #include "TestCase.h" -#include -#include -#include TEST_CASE(testValue) { @@ -50,7 +54,7 @@ TEST_CASE(testValue) { TEST_ASSERT_ALMOST_EQ(v.at(i), 10.0f * i); } - TEST_ASSERT_EQ(v.size(), static_cast(10)); + TEST_ASSERT_EQ(std::ssize(v), 10); // strings v.setContainer(strings); @@ -58,7 +62,7 @@ TEST_CASE(testValue) { TEST_ASSERT_EQ(v.at(i), str::toString(i)); } - TEST_ASSERT_EQ(v.size(), static_cast(10)); + TEST_ASSERT_EQ(std::ssize(v), 10); } TEST_CASE(testChoices) @@ -158,7 +162,7 @@ TEST_CASE(testIterate) std::vector keys; for(cli::Results::const_iterator it = results->begin(); it != results->end(); ++it) keys.push_back(it->first); - TEST_ASSERT_EQ(keys.size(), static_cast(2)); + TEST_ASSERT_EQ(std::ssize(keys), 2); // std::map returns keys in alphabetical order... TEST_ASSERT_EQ(keys[0], "config"); TEST_ASSERT_EQ(keys[1], "verbose"); @@ -168,16 +172,30 @@ TEST_CASE(testRequired) { cli::ArgumentParser parser; parser.setProgram("tester"); - parser.addArgument("-v --verbose", "Toggle verbose", cli::STORE_TRUE); parser.addArgument("-c --config", "Specify a config file", cli::STORE)->setRequired(true); const std::string program(testName); - TEST_EXCEPTION(parser.parse(program, str::split(""))); - TEST_EXCEPTION(parser.parse(program, str::split("-c"))); const auto results = parser.parse(program, str::split("-c configFile")); TEST_ASSERT_EQ(results->get("config"), "configFile"); } +TEST_CASE(testRequiredThrows) +{ + cli::ArgumentParser parser; + parser.setProgram("tester"); + parser.addArgument("-c --config", "Specify a config file", cli::STORE) + ->setRequired(true); + + // The exceptions leak memory which causes an ASAN diagnostic on Linux. + #if CODA_OSS_POSIX_SOURCE && __SANITIZE_ADDRESS__ + TEST_SUCCESS; + #else + const std::string program(testName); + TEST_EXCEPTION(parser.parse(program, str::split(""))); + TEST_EXCEPTION(parser.parse(program, str::split("-c"))); + #endif +} + TEST_CASE(testUnknownArgumentsOptions) { std::ostringstream outStream; @@ -254,6 +272,7 @@ TEST_MAIN( TEST_CHECK( testSubOptions); TEST_CHECK( testIterate); TEST_CHECK( testRequired); + TEST_CHECK( testRequiredThrows); TEST_CHECK( testUnknownArgumentsOptions); ) 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 13cb37dc4d..c69d055c68 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 @@ -23,6 +23,11 @@ #ifndef CODA_OSS_coda_oss_span_h_INCLUDED_ #define CODA_OSS_coda_oss_span_h_INCLUDED_ +#include +#include +#include + +#include #include #include "coda_oss/CPlusPlus.h" @@ -90,6 +95,29 @@ inline span as_writable_bytes(span s) noexcept return span(p, s.size_bytes()); } +// https://en.cppreference.com/w/cpp/iterator/size +template +constexpr size_t size(const C& c) +{ + return c.size(); +} +template +constexpr ptrdiff_t ssize(const C& c) +{ + return gsl::narrow(c.size()); +} + +template +constexpr size_t size(const T (&)[N]) noexcept +{ + return N; +} +template +constexpr ptrdiff_t ssize(const T (&)[N]) noexcept +{ + return N; +} + } #endif // CODA_OSS_coda_oss_span_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/hdf5.lite/unittests/test_highfive.cpp b/externals/coda-oss/modules/c++/hdf5.lite/unittests/test_highfive.cpp index 3ca66b89ae..f54526ab93 100644 --- a/externals/coda-oss/modules/c++/hdf5.lite/unittests/test_highfive.cpp +++ b/externals/coda-oss/modules/c++/hdf5.lite/unittests/test_highfive.cpp @@ -560,7 +560,7 @@ TEST_MAIN( TEST_CHECK(test_highfive_info_nested); TEST_CHECK(test_highfive_dump); - //TEST_CHECK(test_highfive_write); + TEST_CHECK(test_highfive_write); TEST_CHECK(test_highfive_getDataType); TEST_CHECK(test_highfive_getAttribute); diff --git a/externals/coda-oss/modules/c++/io/include/io/ByteStream.h b/externals/coda-oss/modules/c++/io/include/io/ByteStream.h index a64b7efc82..c8fc32a068 100644 --- a/externals/coda-oss/modules/c++/io/include/io/ByteStream.h +++ b/externals/coda-oss/modules/c++/io/include/io/ByteStream.h @@ -117,10 +117,13 @@ struct CODA_OSS_API ByteStream : public SeekableInputStream, public SeekableOutp return mData.empty() ? nullptr : &mData[0]; } - sys::Size_T - getSize() + auto size() const { - return mData.size(); + return mData.size(); + } + auto getSize() const + { + return size(); } protected: diff --git a/externals/coda-oss/modules/c++/io/source/ByteStream.cpp b/externals/coda-oss/modules/c++/io/source/ByteStream.cpp index bffd24aa19..7383011e42 100644 --- a/externals/coda-oss/modules/c++/io/source/ByteStream.cpp +++ b/externals/coda-oss/modules/c++/io/source/ByteStream.cpp @@ -20,6 +20,8 @@ * */ +#include + #include "io/ByteStream.h" sys::Off_T io::ByteStream::seek(sys::Off_T offset, Whence whence) @@ -33,13 +35,13 @@ sys::Off_T io::ByteStream::seek(sys::Off_T offset, Whence whence) mPosition = offset; break; case END: - if (offset > static_cast(mData.size())) + if (offset > std::ssize(mData)) { mPosition = 0; } else { - mPosition = static_cast(mData.size() - offset); + mPosition = std::ssize(mData) - offset; } break; case CURRENT: @@ -48,7 +50,7 @@ sys::Off_T io::ByteStream::seek(sys::Off_T offset, Whence whence) break; } - if (mPosition > static_cast(mData.size())) + if (mPosition > std::ssize(mData)) mPosition = -1; return tell(); } @@ -59,7 +61,7 @@ sys::Off_T io::ByteStream::available() throw except::Exception(Ctxt("Invalid available bytes on eof")); sys::Off_T where = mPosition; - sys::Off_T until = static_cast(mData.size()); + sys::Off_T until = std::ssize(mData); sys::Off_T diff = until - where; return (diff < 0) ? 0 : diff; } @@ -78,7 +80,7 @@ void io::ByteStream::write(const void* buffer, sys::Size_T size) mData.resize(newPos); const auto bufferPtr = static_cast(buffer); - std::copy(bufferPtr, bufferPtr + size, &mData[static_cast(mPosition)]); + std::copy(bufferPtr, bufferPtr + size, &mData[gsl::narrow(mPosition)]); mPosition = static_cast(newPos); } } diff --git a/externals/coda-oss/modules/c++/io/source/RotatingFileOutputStream.cpp b/externals/coda-oss/modules/c++/io/source/RotatingFileOutputStream.cpp index 777c60c009..67a431e537 100644 --- a/externals/coda-oss/modules/c++/io/source/RotatingFileOutputStream.cpp +++ b/externals/coda-oss/modules/c++/io/source/RotatingFileOutputStream.cpp @@ -28,10 +28,11 @@ io::RotatingFileOutputStream::RotatingFileOutputStream( unsigned long maxBytes, size_t backupCount, int creationFlags) : - io::CountingOutputStream(new io::FileOutputStream(filename, creationFlags), - true), mFilename(filename), mMaxBytes(maxBytes), - mBackupCount(backupCount) + io::CountingOutputStream(new io::FileOutputStream(filename, creationFlags), true), + mMaxBytes(maxBytes), mBackupCount(backupCount) { + mFilename = filename; // doing this in initializer list causes ASAN diagnostic on Windows ... VS bug? + mByteCount = ((io::FileOutputStream*) mProxy.get())->tell(); if (shouldRollover(0)) doRollover(); diff --git a/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp b/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp index 63587403e7..f3d777ed89 100644 --- a/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp +++ b/externals/coda-oss/modules/c++/io/unittests/test_streams.cpp @@ -80,10 +80,10 @@ TEST_CASE(testByteStream) stream.seek(2, io::Seekable::END); TEST_ASSERT_EQ(stream.tell(), 18); - TEST_ASSERT_EQ(stream.getSize(), static_cast(20)); + TEST_ASSERT_EQ(std::ssize(stream), 20); stream.write("abcdef"); - TEST_ASSERT_EQ(stream.getSize(), static_cast(24)); + TEST_ASSERT_EQ(std::ssize(stream), 24); const std::string test("test"); { diff --git a/externals/coda-oss/modules/c++/logging/unittests/test_exception_logger.cpp b/externals/coda-oss/modules/c++/logging/unittests/test_exception_logger.cpp index 57016a0de9..6084f1d87a 100644 --- a/externals/coda-oss/modules/c++/logging/unittests/test_exception_logger.cpp +++ b/externals/coda-oss/modules/c++/logging/unittests/test_exception_logger.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "TestCase.h" @@ -102,7 +103,7 @@ TEST_CASE(testExceptionWithBacktrace) } catch (const except::Throwable& t) { - TEST_ASSERT_EQ(t.getBacktrace().size(), static_cast(0)); + TEST_ASSERT_EQ(std::ssize(t.getBacktrace()), 0); s = t.toString(); what = t.what(); } diff --git a/externals/coda-oss/modules/c++/math.linear/unittests/test_Vector.cpp b/externals/coda-oss/modules/c++/math.linear/unittests/test_Vector.cpp index 1a4df6d46e..4e912b21da 100644 --- a/externals/coda-oss/modules/c++/math.linear/unittests/test_Vector.cpp +++ b/externals/coda-oss/modules/c++/math.linear/unittests/test_Vector.cpp @@ -1,3 +1,4 @@ +#include #include #include "TestCase.h" #include "math/linear/Vector.h" @@ -256,8 +257,8 @@ TEST_CASE(testOperatorMinusEquals) Vector v2(5, -5); v2 -= v1; - TEST_ASSERT_EQ(v1.size(), static_cast(5)); - TEST_ASSERT_EQ(v2.size(), static_cast(5)); + TEST_ASSERT_EQ(std::ssize(v1), 5); + TEST_ASSERT_EQ(std::ssize(v2), 5); for (int i = 0; i < 5; i++) TEST_ASSERT_EQ(v2[i], -18); for (int i = 0; i < 5; i++) @@ -347,9 +348,9 @@ TEST_CASE(testOperatorMinus) Vector v3(v2 - v1); // TODO: Test what happens if v1 & v2 are of different lengths. - TEST_ASSERT_EQ(v1.size(), static_cast(4)); - TEST_ASSERT_EQ(v2.size(), static_cast(4)); - TEST_ASSERT_EQ(v3.size(), static_cast(4)); + TEST_ASSERT_EQ(std::ssize(v1), 4); + TEST_ASSERT_EQ(std::ssize(v2), 4); + TEST_ASSERT_EQ(std::ssize(v3), 4); for (int i = 0; i < 4; i++) { diff --git a/externals/coda-oss/modules/c++/mt/include/mt/CPUAffinityInitializerLinux.h b/externals/coda-oss/modules/c++/mt/include/mt/CPUAffinityInitializerLinux.h index cde51eb615..494f6fb5e6 100644 --- a/externals/coda-oss/modules/c++/mt/include/mt/CPUAffinityInitializerLinux.h +++ b/externals/coda-oss/modules/c++/mt/include/mt/CPUAffinityInitializerLinux.h @@ -40,6 +40,7 @@ namespace mt struct AbstractNextCPUProviderLinux { virtual std::unique_ptr nextCPU() = 0; + virtual ~AbstractNextCPUProviderLinux() {} }; /*! diff --git a/externals/coda-oss/modules/c++/mt/source/CPUAffinityInitializerLinux.cpp b/externals/coda-oss/modules/c++/mt/source/CPUAffinityInitializerLinux.cpp index 01181be6b7..7da70537a7 100644 --- a/externals/coda-oss/modules/c++/mt/source/CPUAffinityInitializerLinux.cpp +++ b/externals/coda-oss/modules/c++/mt/source/CPUAffinityInitializerLinux.cpp @@ -51,14 +51,14 @@ std::vector mergeAvailableCPUs() namespace mt { -class AvailableCPUProvider : public AbstractNextCPUProviderLinux +struct AvailableCPUProvider final : public AbstractNextCPUProviderLinux { -public: AvailableCPUProvider() : mCPUs(mergeAvailableCPUs()), mNextCPUIndex(0) { } + ~AvailableCPUProvider() = default; virtual std::unique_ptr nextCPU() override { @@ -79,13 +79,13 @@ class AvailableCPUProvider : public AbstractNextCPUProviderLinux size_t mNextCPUIndex; }; -class OffsetCPUProvider : public AbstractNextCPUProviderLinux +struct OffsetCPUProvider final : public AbstractNextCPUProviderLinux { -public: OffsetCPUProvider(int initialOffset) : mNextCPU(initialOffset) { } + ~OffsetCPUProvider() = default; virtual std::unique_ptr nextCPU() override { diff --git a/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp b/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp index d70885e3e8..94fd0b9ec9 100644 --- a/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp +++ b/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp @@ -330,6 +330,19 @@ void Regex::split(const std::string& str, std::vector& v) } } +inline static void replace(std::string& result, size_t pos, size_t count, const std::string& str) +{ + // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 + #if _WIN32 && __SANITIZE_ADDRESS__ && (_MSC_VER <= 1933 /*VS 2022 17.3*/) + // ASAN diagnostic on Windows with replace() ... bug in VS? + const auto input = result; + result = input.substr(0, pos); + result += str; + result += input.substr(pos + count); + #else + result.replace(pos, count, str); + #endif +} std::string Regex::sub(const std::string& str, const std::string& repl) { size_t begin; @@ -339,7 +352,7 @@ std::string Regex::sub(const std::string& str, const std::string& repl) std::string result = search(str, startIndex, 0, begin, end); while (!result.empty()) { - toReplace.replace(begin, result.size(), repl); + replace(toReplace, begin, result.size(), repl); // You can't skip ahead result.size() here because 'repl' may be shorter // than 'result' diff --git a/externals/coda-oss/modules/c++/sio.lite/include/sio/lite/UserDataDictionary.h b/externals/coda-oss/modules/c++/sio.lite/include/sio/lite/UserDataDictionary.h index 627c975f81..00aae905b2 100644 --- a/externals/coda-oss/modules/c++/sio.lite/include/sio/lite/UserDataDictionary.h +++ b/externals/coda-oss/modules/c++/sio.lite/include/sio/lite/UserDataDictionary.h @@ -77,6 +77,7 @@ class OrderedDictionary } virtual size_t size() const { return mList.size(); } + virtual bool empty() const { return mList.empty(); } virtual void add(Key_T key, const Value_T& value) { diff --git a/externals/coda-oss/modules/c++/sio.lite/source/FileHeader.cpp b/externals/coda-oss/modules/c++/sio.lite/source/FileHeader.cpp index 8a7c8a40fc..619f8af72b 100644 --- a/externals/coda-oss/modules/c++/sio.lite/source/FileHeader.cpp +++ b/externals/coda-oss/modules/c++/sio.lite/source/FileHeader.cpp @@ -65,7 +65,7 @@ long sio::lite::FileHeader::getLength() const { size_t length = SIO_HEADER_LENGTH; - if (userData.size() > 0) + if (!userData.empty()) length += 4; //num fields int for (sio::lite::UserDataDictionary::ConstIterator it = userData.begin(); it != userData.end(); ++it) @@ -158,7 +158,7 @@ void sio::lite::FileHeader::to(size_t numBands, io::OutputStream& os) void sio::lite::FileHeader::writeUserData(io::OutputStream& os) { - const auto numFields = static_cast(userData.size()); + const auto numFields = gsl::narrow(userData.size()); os.write((const sys::byte*)&numFields, 4); for(sio::lite::UserDataDictionary::Iterator it = userData.begin(); @@ -171,7 +171,7 @@ void sio::lite::FileHeader::writeUserData(io::OutputStream& os) os.write((const sys::byte*)key.c_str(), keySize); std::vector& uData = it->second; - const auto udSize = static_cast(uData.size()); + const auto udSize = gsl::narrow(uData.size()); os.write((const sys::byte*)&udSize, 4); //Do we need to check for endian-ness and possibly byteswap??? diff --git a/externals/coda-oss/modules/c++/std/include/std/span b/externals/coda-oss/modules/c++/std/include/std/span index 407d7d54f9..6eb29377b2 100644 --- a/externals/coda-oss/modules/c++/std/include/std/span +++ b/externals/coda-oss/modules/c++/std/include/std/span @@ -23,6 +23,7 @@ #define CODA_OSS_std_span_INCLUDED_ #include "coda_oss/span.h" +#include "coda_oss/CPlusPlus.h" // Make it (too?) easy for clients to get our various std:: implementations #ifndef CODA_OSS_NO_std_span @@ -47,4 +48,50 @@ namespace std // This is slightly uncouth: we're not supposed to augment "std". #endif // CODA_OSS_NO_std_span +// Make it (too?) easy for clients to get our various std:: implementations +#ifndef CODA_OSS_NO_nonmember_container_access + // https://en.cppreference.com/w/cpp/feature_test#cpp_lib_nonmember_container_access + #if defined(__cpp_lib_nonmember_container_access) && (__cpp_lib_nonmember_container_access >= 201411L) + #define CODA_OSS_NO_nonmember_container_access 1 // no need to muck with `std` + #else + // It seems MSVC has this even at C++14, see + // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 + #if _MSC_VER >= 1900 // Visual Studio 2015 (14.0) + #define CODA_OSS_NO_nonmember_container_access 1 // no need to muck with `std` + #else + #define CODA_OSS_NO_nonmember_container_access 0 // use our own + #endif + #endif +#endif + +#if !CODA_OSS_NO_nonmember_container_access +namespace std // This is slightly uncouth: we're not supposed to augment "std". +{ + using coda_oss::size; +} +#ifndef __cpp_lib_nonmember_container_access +#define __cpp_lib_nonmember_container_access 201411L // https://en.cppreference.com/w/cpp/feature_test +#endif +#endif // CODA_OSS_NO_nonmember_container_access + +// Make it (too?) easy for clients to get our various std:: implementations +#ifndef CODA_OSS_NO_std_ssize + // https://en.cppreference.com/w/cpp/feature_test#cpp_lib_ssize + #if defined(__cpp_lib_ssize) && (__cpp_lib_ssize >= 201902L) + #define CODA_OSS_NO_std_ssize 1 // no need to muck with `std` + #else + #define CODA_OSS_NO_std_ssize 0 // use our own + #endif +#endif + +#if !CODA_OSS_NO_std_ssize +namespace std // This is slightly uncouth: we're not supposed to augment "std". +{ + using coda_oss::ssize; +} +#ifndef __cpp_lib_ssize +#define __cpp_lib_ssize 201902L // https://en.cppreference.com/w/cpp/feature_test +#endif +#endif // CODA_OSS_NO_std_ssize + #endif // CODA_OSS_std_span_INCLUDED_ \ No newline at end of file diff --git a/externals/coda-oss/modules/c++/str/unittests/test_str.cpp b/externals/coda-oss/modules/c++/str/unittests/test_str.cpp index 8fd60b90dd..2d00862d8e 100644 --- a/externals/coda-oss/modules/c++/str/unittests/test_str.cpp +++ b/externals/coda-oss/modules/c++/str/unittests/test_str.cpp @@ -21,6 +21,7 @@ */ #include // std::ignore +#include #include #include @@ -125,9 +126,9 @@ TEST_CASE(testSplit) { std::string s = "space delimited values are the best!"; std::vector parts = str::split(s, " "); - TEST_ASSERT_EQ(parts.size(), static_cast(6)); + TEST_ASSERT_EQ(std::ssize(parts), 6); parts = str::split(s, " ", 3); - TEST_ASSERT_EQ(parts.size(), static_cast(3)); + TEST_ASSERT_EQ(std::ssize(parts), 3); TEST_ASSERT_EQ(parts[2], "values are the best!"); } diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_NaN_testing.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_NaN_testing.cpp index 82df405095..859e37b209 100644 --- a/externals/coda-oss/modules/c++/sys/unittests/test_NaN_testing.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_NaN_testing.cpp @@ -21,6 +21,9 @@ */ #include +#include +#include + #include "TestCase.h" TEST_CASE(testNaNsAreNotEqual) @@ -58,9 +61,40 @@ TEST_CASE(testIsNaN) TEST_ASSERT_FALSE(IS_NAN(std::string("test string"))); } +TEST_CASE(test_ssize) +{ + // https://en.cppreference.com/w/cpp/iterator/size + + // Works with containers + std::vector v{3, 1, 4}; + TEST_ASSERT_EQ(std::size(v), 3); + + // And works with built-in arrays too + int a[]{-5, 10, 15}; + // Returns the number of elements (not bytes) as opposed to sizeof + TEST_ASSERT_EQ(std::size(a), 3); + static_assert(sizeof(a) == 12, "sizeof(a)"); + + // Provides a safe way (compared to sizeof) of getting string buffer size + const char str[] = "12345"; + // These are fine and give the correct result + TEST_ASSERT_EQ(std::size(str), 6); + static_assert(sizeof(str) == 6, "sizeof(str)"); + + // But use of sizeof here is a common source of bugs + const char* str_decayed = "12345"; + static_assert(sizeof(str_decayed) == sizeof(void*), "sizeof(void*)"); + + // Since C++20 the signed size (std::ssize) is available + auto i = std::ssize(v); + for (--i; i != -1; --i) { } + TEST_ASSERT_EQ(i, -1); +} + TEST_MAIN( TEST_CHECK(testNaNsAreNotEqual); TEST_CHECK(testNaNIsNotAlmostEqualToNumber); TEST_CHECK(testIsNaN); -) + TEST_CHECK(test_ssize); + ) diff --git a/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlelement.cpp b/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlelement.cpp index 0c252e12a7..2f42d5b386 100644 --- a/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlelement.cpp +++ b/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlelement.cpp @@ -21,6 +21,7 @@ */ #include +#include #include "coda_oss/CPlusPlus.h" #include "io/StringStream.h" #include @@ -101,7 +102,7 @@ TEST_CASE(test_getElementsByTagName) { const auto aElements = root.getElementsByTagName("a", true /*recurse*/); - TEST_ASSERT_EQ(aElements.size(), static_cast(1)); + TEST_ASSERT_EQ(std::ssize(aElements), 1); const auto& a = *(aElements[0]); const auto characterData = a.getCharacterData(); @@ -113,7 +114,7 @@ TEST_CASE(test_getElementsByTagName) TEST_ASSERT_EQ(docElements.size(), static_cast(1)); { const auto aElements = docElements[0]->getElementsByTagName("a"); - TEST_ASSERT_EQ(aElements.size(), static_cast(1)); + TEST_ASSERT_EQ(std::ssize(aElements), 1); const auto& a = *(aElements[0]); const auto characterData = a.getCharacterData(); @@ -137,10 +138,10 @@ TEST_CASE(test_getElementsByTagName_duplicate) const auto docElements = root.getElementsByTagName("doc"); TEST_ASSERT_FALSE(docElements.empty()); - TEST_ASSERT_EQ(docElements.size(), static_cast(1)); + TEST_ASSERT_EQ(std::ssize(docElements), 1); { const auto duplicateElements = docElements[0]->getElementsByTagName("duplicate"); - TEST_ASSERT_EQ(duplicateElements.size(), static_cast(2)); + TEST_ASSERT_EQ(std::ssize(duplicateElements), 2); const auto& duplicate = *(duplicateElements[0]); const auto characterData = duplicate.getCharacterData(); diff --git a/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlparser.cpp b/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlparser.cpp index 3dad8420c2..e35911651a 100644 --- a/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlparser.cpp +++ b/externals/coda-oss/modules/c++/xml.lite/unittests/test_xmlparser.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "io/StringStream.h" #include "io/FileInputStream.h" @@ -105,7 +106,7 @@ static std::filesystem::path find_unittest_file(const std::filesystem::path& nam static void test_a_element(const std::string& testName, const xml::lite::Element& root) { const auto aElements = root.getElementsByTagName("a", true /*recurse*/); - TEST_ASSERT_EQ(aElements.size(), static_cast(1)); + TEST_ASSERT_EQ(std::ssize(aElements), 1); const auto& a = *(aElements[0]); const auto characterData = a.getCharacterData(); @@ -124,7 +125,7 @@ TEST_CASE(testXmlParseSimple) const auto docElements = root.getElementsByTagName("doc"); TEST_ASSERT_FALSE(docElements.empty()); - TEST_ASSERT_EQ(docElements.size(), static_cast(1)); + TEST_ASSERT_EQ(std::ssize(docElements), 1); test_a_element(testName, *docElements[0]); } @@ -365,7 +366,7 @@ static void testReadXmlFile(const std::string& testName, const std::string& xmlF const auto& root = getRootElement(getDocument(xmlParser)); const auto aElements = root.getElementsByTagName("a", true /*recurse*/); - TEST_ASSERT_EQ(aElements.size(), static_cast(1)); + TEST_ASSERT_EQ(std::ssize(aElements), 1); const auto& a = *(aElements[0]); auto characterData = a.getCharacterData(); diff --git a/externals/coda-oss/modules/c++/zip/include/zip/ZipFile.h b/externals/coda-oss/modules/c++/zip/include/zip/ZipFile.h index 6e7c7e6e6c..b81927d745 100644 --- a/externals/coda-oss/modules/c++/zip/include/zip/ZipFile.h +++ b/externals/coda-oss/modules/c++/zip/include/zip/ZipFile.h @@ -23,6 +23,8 @@ #ifndef __ZIP_ZIP_FILE_H__ #define __ZIP_ZIP_FILE_H__ +#include "gsl/gsl.h" + #include "zip/ZipEntry.h" /*! @@ -144,9 +146,9 @@ class ZipFile return mComment; } - unsigned long getNumEntries() const + auto getNumEntries() const { - return static_cast(mEntries.size()); + return gsl::narrow(mEntries.size()); } }; diff --git a/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp b/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp index 87358d8e14..b9fb666f50 100644 --- a/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp +++ b/externals/coda-oss/modules/drivers/highfive/unittests/tests_high_five_base.cpp @@ -2824,6 +2824,10 @@ TEST_CASE(HighFiveEnum) { CHECK(result == Position::highfive_first); } + // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 + #if _WIN32 && __SANITIZE_ADDRESS__ && (_MSC_VER <= 1933 /*VS 2022 17.3*/) + // Bug in VS ASAN? + #else { // Scoped enum auto e1 = create_enum_direction(); e1.commit(file, "Direction"); @@ -2847,6 +2851,7 @@ TEST_CASE(HighFiveEnum) { CHECK(result[3] == Direction::Left); CHECK(result[4] == Direction::Left); } + #endif } TEST_CASE(HighFiveFixedString) { diff --git a/externals/coda-oss/modules/drivers/uuid/CMakeLists.txt b/externals/coda-oss/modules/drivers/uuid/CMakeLists.txt index a041f1452a..0a437f6e05 100644 --- a/externals/coda-oss/modules/drivers/uuid/CMakeLists.txt +++ b/externals/coda-oss/modules/drivers/uuid/CMakeLists.txt @@ -42,7 +42,7 @@ else() coda_fetch_driver( NAME ${TARGET_NAME} ARCHIVE "e2fsprogs-1.47.0.tar" - HASH "SHA256=dbf5661535cd19f2eab0915442b2ad2b8f1f471b039b92c79bed7f53bab8dccb" + HASH "SHA256=c7c3a26bee8bb3b0041ee8701a5f47a29e81ee0b20d5995bc4544638cd0ea394" ) set(SOURCE_DIR "${${CMAKE_PROJECT_NAME}_${TARGET_NAME}_SOURCE_DIR}") diff --git a/externals/coda-oss/modules/drivers/uuid/e2fsprogs-1.47.0.tar b/externals/coda-oss/modules/drivers/uuid/e2fsprogs-1.47.0.tar index 1c839924ae01048c4dc7a89b75e46e338094dda7..9d1bfa2cea1e6ab9f841a7e52b1e09fa07dce339 100644 GIT binary patch delta 24611 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 delta 24559 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