diff --git a/externals/coda-oss/modules/c++/config/include/config/coda_oss_config.h.in b/externals/coda-oss/modules/c++/config/include/config/coda_oss_config.h.in index 14fca616b..24d164881 100644 --- a/externals/coda-oss/modules/c++/config/include/config/coda_oss_config.h.in +++ b/externals/coda-oss/modules/c++/config/include/config/coda_oss_config.h.in @@ -3,6 +3,7 @@ #ifndef _CODA_OSS_CONFIG_H_ #define _CODA_OSS_CONFIG_H_ +/* these should no longer be needed */ #cmakedefine HAVE_PTHREAD_H @HAVE_PTHREAD_H@ #cmakedefine HAVE_EXECINFO_H @HAVE_EXECINFO_H@ #cmakedefine HAVE_CLOCK_GETTIME @HAVE_CLOCK_GETTIME@ @@ -11,12 +12,14 @@ #cmakedefine HAVE_LOCALTIME_R @HAVE_LOCALTIME_R@ #cmakedefine HAVE_GMTIME_R @HAVE_GMTIME_R@ #cmakedefine HAVE_SETENV @HAVE_SETENV@ -#cmakedefine HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ -#cmakedefine HAVE_MEMALIGN @HAVE_MEMALIGN@ #cmakedefine01 BIGENDIAN #cmakedefine SIZEOF_SIZE_T @SIZEOF_SIZE_T@ -#cmakedefine HAVE_ATTRIBUTE_NOINLINE @HAVE_ATTRIBUTE_NOINLINE@ +#cmakedefine HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ +#cmakedefine HAVE_MEMALIGN @HAVE_MEMALIGN@ #cmakedefine HAVE_ATTRIBUTE_ALIGNED @HAVE_ATTRIBUTE_ALIGNED@ +#cmakedefine HAVE_ATTRIBUTE_NOINLINE @HAVE_ATTRIBUTE_NOINLINE@ + +/* still might need these ... for now */ #cmakedefine CODA_EXPORT @CODA_EXPORT@ #endif /* _CODA_OSS_CONFIG_H_ */ diff --git a/externals/coda-oss/modules/c++/config/include/config/compiler_extensions.h b/externals/coda-oss/modules/c++/config/include/config/compiler_extensions.h index d6f295b0b..93c445d82 100644 --- a/externals/coda-oss/modules/c++/config/include/config/compiler_extensions.h +++ b/externals/coda-oss/modules/c++/config/include/config/compiler_extensions.h @@ -19,21 +19,34 @@ * see . * */ -#ifndef CONFIG_COMPILER_EXTENSIONS -#define CONFIG_COMPILER_EXTENSIONS +#ifndef CODA_OSS_config_compiler_extentions_h_INCLUDED_ +#define CODA_OSS_config_compiler_extentions_h_INCLUDED_ +#pragma once #include -#ifdef HAVE_ATTRIBUTE_NOINLINE -#define ATTRIBUTE_NOINLINE __attribute__((noinline)) -#else -#define ATTRIBUTE_NOINLINE -#endif +#ifndef CODA_OSS_attribute_noinline_DEFINED_ + #define CODA_OSS_attribute_noinline_DEFINED_ 1 -#ifdef HAVE_ATTRIBUTE_ALIGNED -#define ATTRIBUTE_ALIGNED(x) __attribute__((aligned (x))) -#else -#define ATTRIBUTE_ALIGNED(X) -#endif + // https://stackoverflow.com/a/49468469/8877 + #if defined(__GNUC__) + #define ATTRIBUTE_NOINLINE __attribute__((noinline)) + #elif defined(_MSC_VER) + #define ATTRIBUTE_NOINLINE __declspec(noinline) + #else + #define ATTRIBUTE_NOINLINE + #endif +#endif // CODA_OSS_attribute_noinline_DEFINED_ -#endif +#ifndef CODA_OSS_attribute_aligned_DEFINED_ + #define CODA_OSS_attribute_aligned_DEFINED_ 1 + + #if defined(__GNUC__) + // https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html + #define ATTRIBUTE_ALIGNED(x) __attribute__((aligned(x))) + #else + #define ATTRIBUTE_ALIGNED(X) + #endif +#endif // CODA_OSS_attribute_aligned_DEFINED_ + +#endif // CODA_OSS_config_compiler_extentions_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/io/include/io/PipeStream.h b/externals/coda-oss/modules/c++/io/include/io/PipeStream.h index 5795a79b0..1f2299b27 100644 --- a/externals/coda-oss/modules/c++/io/include/io/PipeStream.h +++ b/externals/coda-oss/modules/c++/io/include/io/PipeStream.h @@ -39,11 +39,8 @@ namespace io * \brief captures the standard output from a pipe * and streams it to the specified location */ -class PipeStream : InputStream +struct PipeStream : InputStream { - -public: - /*! * Constructor -- * Streams data from a pipe when available @@ -95,6 +92,9 @@ class PipeStream : InputStream virtual sys::SSize_T streamTo(OutputStream& soi, sys::SSize_T numBytes = IS_END); + PipeStream(const PipeStream&) = delete; + PipeStream& operator=(const PipeStream&) = delete; + protected: /*! * \brief returns the requested size in bytes from the stream @@ -104,13 +104,7 @@ class PipeStream : InputStream sys::ExecPipe mExecPipe; mem::ScopedArray mCharString; - size_t mBufferSize; - -private: - - //! Noncopyable - PipeStream(const PipeStream& ); - const PipeStream& operator=(const PipeStream& ); + size_t mBufferSize = DEFAULT_CHUNK_SIZE; }; } diff --git a/externals/coda-oss/modules/c++/io/include/io/TempFile.h b/externals/coda-oss/modules/c++/io/include/io/TempFile.h index 7f1d9dde0..b08c0a1ac 100644 --- a/externals/coda-oss/modules/c++/io/include/io/TempFile.h +++ b/externals/coda-oss/modules/c++/io/include/io/TempFile.h @@ -32,9 +32,8 @@ namespace io * RAII object for a temporary file that gets deleted * upon object destruction */ -class TempFile +struct TempFile { -public: /*! * Constructor for TempFile object. Provided a directory, * this will find a random, unused filename, and create a file @@ -53,10 +52,11 @@ class TempFile { return mPathname; } + + TempFile(const TempFile&) = delete; + TempFile& operator=(const TempFile&) = delete; + private: - // Noncopyable - TempFile(const TempFile& ); - const TempFile& operator=(const TempFile& ); const sys::OS mOS; const std::string mPathname; }; diff --git a/externals/coda-oss/modules/c++/logging/include/logging/Logger.h b/externals/coda-oss/modules/c++/logging/include/logging/Logger.h index f1e2ca9ed..c946af30e 100644 --- a/externals/coda-oss/modules/c++/logging/include/logging/Logger.h +++ b/externals/coda-oss/modules/c++/logging/include/logging/Logger.h @@ -43,10 +43,8 @@ namespace logging * Instances of the Logger class represent a single logging channel. * A Logger instance can log to several Handlers. */ -class Logger : public Filterer +struct Logger : public Filterer { - -public: /*! * Constructs a Logger with an optional name * \param name (optional) Name of the logger @@ -130,15 +128,13 @@ class Logger : public Filterer //! Removes all handlers void reset(); -private: - // Noncopyable // NOTE: It isn't currently safe to copy a logger because mHandlers isn't // a deep copy and you end up with a double delete (it's not using // smart pointers :o( ). If we really wanted to support a copy, // would need to decide if mHandlers should be deeply or shallowly // copied. - Logger(const Logger& ); - Logger& operator=(const Logger& ); + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; protected: void handle(const LogRecord* record); diff --git a/externals/coda-oss/modules/c++/mem/include/mem/ScopedAlignedArray.h b/externals/coda-oss/modules/c++/mem/include/mem/ScopedAlignedArray.h index c0e55e5ad..429cc50c8 100644 --- a/externals/coda-oss/modules/c++/mem/include/mem/ScopedAlignedArray.h +++ b/externals/coda-oss/modules/c++/mem/include/mem/ScopedAlignedArray.h @@ -34,9 +34,8 @@ namespace mem * \brief This class provides RAII for alignedAlloc() and alignedFree() */ template - class ScopedAlignedArray + struct ScopedAlignedArray { - public: typedef T ElementType; explicit ScopedAlignedArray( @@ -91,11 +90,10 @@ namespace mem return array; } - private: - // Noncopyable - ScopedAlignedArray(const ScopedAlignedArray& ); - const ScopedAlignedArray& operator=(const ScopedAlignedArray& ); + ScopedAlignedArray(const ScopedAlignedArray&) = delete; + ScopedAlignedArray& operator=(const ScopedAlignedArray&) = delete; + private: static T* allocate(size_t numElements, size_t alignment) { diff --git a/externals/coda-oss/modules/c++/mem/include/mem/ScopedArray.h b/externals/coda-oss/modules/c++/mem/include/mem/ScopedArray.h index 4fa6b8c05..55d710add 100644 --- a/externals/coda-oss/modules/c++/mem/include/mem/ScopedArray.h +++ b/externals/coda-oss/modules/c++/mem/include/mem/ScopedArray.h @@ -33,9 +33,8 @@ namespace mem * It is based on boost::scoped_array. */ template - class ScopedArray - { - public: + struct ScopedArray + { typedef T ElementType; explicit ScopedArray(T* array = NULL) : @@ -71,10 +70,8 @@ namespace mem return array; } - private: - // Noncopyable - ScopedArray(const ScopedArray& ); - const ScopedArray& operator=(const ScopedArray& ); + ScopedArray(const ScopedArray&) = delete; + ScopedArray& operator=(const ScopedArray&) = delete; private: T* mArray; diff --git a/externals/coda-oss/modules/c++/mem/include/mem/ScratchMemory.h b/externals/coda-oss/modules/c++/mem/include/mem/ScratchMemory.h index 77c66a40f..2eb5c9113 100644 --- a/externals/coda-oss/modules/c++/mem/include/mem/ScratchMemory.h +++ b/externals/coda-oss/modules/c++/mem/include/mem/ScratchMemory.h @@ -157,10 +157,10 @@ class ScratchMemory return mNumBytesNeeded; } -private: - ScratchMemory(const ScratchMemory&); - ScratchMemory& operator=(const ScratchMemory&); + ScratchMemory(const ScratchMemory&) = delete; + ScratchMemory& operator=(const ScratchMemory&) = delete; +private: struct Segment { Segment(size_t numBytes, size_t numBuffers, size_t alignment, size_t offset); diff --git a/externals/coda-oss/modules/c++/mem/include/mem/VectorOfPointers.h b/externals/coda-oss/modules/c++/mem/include/mem/VectorOfPointers.h index e26a58c37..acded251c 100644 --- a/externals/coda-oss/modules/c++/mem/include/mem/VectorOfPointers.h +++ b/externals/coda-oss/modules/c++/mem/include/mem/VectorOfPointers.h @@ -37,9 +37,8 @@ namespace mem * \brief This class provides safe cleanup for vectors of pointers */ template -class VectorOfPointers +struct VectorOfPointers { -public: VectorOfPointers() { } @@ -149,19 +148,16 @@ class VectorOfPointers return mValues.erase(first, last); } -private: - // Noncopyable - VectorOfPointers(const VectorOfPointers& ); - const VectorOfPointers& operator=(const VectorOfPointers& ); + VectorOfPointers(const VectorOfPointers&) = delete; + VectorOfPointers& operator=(const VectorOfPointers&) = delete; private: std::vector mValues; }; template - class VectorOfSharedPointers + struct VectorOfSharedPointers { -public: VectorOfSharedPointers() { } diff --git a/externals/coda-oss/modules/c++/mem/unittests/test_scoped_copyable_ptr.cpp b/externals/coda-oss/modules/c++/mem/unittests/test_scoped_copyable_ptr.cpp index bf3fa2594..ff0747f92 100644 --- a/externals/coda-oss/modules/c++/mem/unittests/test_scoped_copyable_ptr.cpp +++ b/externals/coda-oss/modules/c++/mem/unittests/test_scoped_copyable_ptr.cpp @@ -20,38 +20,34 @@ * */ +#include + #include #include "TestCase.h" namespace { -struct Foo +struct Foo final { - Foo() - : val1(0), - val2(0) - { - } - - size_t val1; - size_t val2; + size_t val1 = 0; + size_t val2 = 0; }; -struct Bar +struct Bar final { - Bar() - : val3(0) - { - } - mem::ScopedCopyablePtr foo; - size_t val3; + size_t val3 = 0; +}; + +struct Baz final +{ + std::shared_ptr pFoo; + size_t val3 = 0; }; -class AssignOnDestruct +struct AssignOnDestruct final { -public: AssignOnDestruct(size_t &ref, size_t finalVal) : mRef(ref), mFinalVal(finalVal) @@ -92,6 +88,25 @@ TEST_CASE(testCopyConstructor) TEST_ASSERT_EQ(bar1.val3, 30); } +TEST_CASE(testSharedCopyConstructor) +{ + // Initialize the values + Baz b1; + b1.pFoo.reset(new Foo()); + b1.pFoo->val1 = 10; + b1.pFoo->val2 = 20; + b1.val3 = 30; + + // Show that memory is shared, not copied as with mem::ScopedCopyablePtr + auto b2 = b1; + b2.pFoo->val1 = 40; + b2.pFoo->val2 = 50; + b2.val3 = 60; + TEST_ASSERT_EQ(b1.pFoo->val1, 40); + TEST_ASSERT_EQ(b1.pFoo->val2, 50); + TEST_ASSERT_EQ(b1.val3, 30); +} + TEST_CASE(testAssignmentOperator) { // Initialize the values @@ -117,6 +132,27 @@ TEST_CASE(testAssignmentOperator) TEST_ASSERT_EQ(bar1.val3, 30); } +TEST_CASE(testSharedAssignmentOperator) +{ + // Initialize the values + Baz b1; + b1.pFoo.reset(new Foo()); + b1.pFoo->val1 = 10; + b1.pFoo->val2 = 20; + b1.val3 = 30; + + Baz b2; + b2 = b1; + + // Show that memory is shared, not copied as with mem::ScopedCopyablePtr + b2.pFoo->val1 = 40; + b2.pFoo->val2 = 50; + b2.val3 = 60; + TEST_ASSERT_EQ(b1.pFoo->val1, 40); + TEST_ASSERT_EQ(b1.pFoo->val2, 50); + TEST_ASSERT_EQ(b1.val3, 30); +} + TEST_CASE(testDestructor) { // When the ScopedCopyablePtr goes out of scope, it should delete the @@ -167,7 +203,9 @@ TEST_CASE(testEqualityOperator) int main(int, char**) { TEST_CHECK(testCopyConstructor); + TEST_CHECK(testSharedCopyConstructor); TEST_CHECK(testAssignmentOperator); + TEST_CHECK(testSharedAssignmentOperator); TEST_CHECK(testDestructor); TEST_CHECK(testSyntax); TEST_CHECK(testEqualityOperator); diff --git a/externals/coda-oss/modules/c++/mt/include/mt/CriticalSection.h b/externals/coda-oss/modules/c++/mt/include/mt/CriticalSection.h index 69ce39b12..b4aeb42c0 100644 --- a/externals/coda-oss/modules/c++/mt/include/mt/CriticalSection.h +++ b/externals/coda-oss/modules/c++/mt/include/mt/CriticalSection.h @@ -51,9 +51,8 @@ namespace mt * } * \endcode */ -template class CriticalSection +template struct CriticalSection { -public: //! Constructor. Lock the mutex. CriticalSection(T* mutex) : mMutex(mutex), @@ -92,10 +91,8 @@ template class CriticalSection mIsLocked = true; } -private: - // Noncopyable - CriticalSection(const CriticalSection& ); - const CriticalSection& operator=(const CriticalSection& ); + CriticalSection(const CriticalSection&) = delete; + CriticalSection& operator=(const CriticalSection&) = delete; private: T* const mMutex; diff --git a/externals/coda-oss/modules/c++/mt/include/mt/RequestQueue.h b/externals/coda-oss/modules/c++/mt/include/mt/RequestQueue.h index 313ee2104..cb0d8201c 100644 --- a/externals/coda-oss/modules/c++/mt/include/mt/RequestQueue.h +++ b/externals/coda-oss/modules/c++/mt/include/mt/RequestQueue.h @@ -49,10 +49,8 @@ namespace mt */ template -class RequestQueue +struct RequestQueue { -public: - //! Default constructor RequestQueue() : mAvailableSpace(&mQueueLock), @@ -128,10 +126,8 @@ class RequestQueue mAvailableSpace.signal(); } -private: - // Noncopyable - RequestQueue(const RequestQueue& ); - const RequestQueue& operator=(const RequestQueue& ); + RequestQueue(const RequestQueue&) = delete; + RequestQueue& operator=(const RequestQueue&) = delete; private: //! The internal data structure diff --git a/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp b/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp index 44e72bb8f..e641e2d57 100644 --- a/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp +++ b/externals/coda-oss/modules/c++/re/source/RegexPCRE.cpp @@ -139,10 +139,8 @@ class ScopedMatchData return str.substr(index, subStringLength); } -private: - // Noncopyable - ScopedMatchData(const ScopedMatchData& ); - ScopedMatchData& operator=(const ScopedMatchData& ); + ScopedMatchData(const ScopedMatchData&) = delete; + ScopedMatchData& operator=(const ScopedMatchData&) = delete; private: const pcre2_code* const mCode; diff --git a/externals/coda-oss/modules/c++/str/include/str/Manip.h b/externals/coda-oss/modules/c++/str/include/str/Manip.h index 9d404e97c..c7dda96f6 100644 --- a/externals/coda-oss/modules/c++/str/include/str/Manip.h +++ b/externals/coda-oss/modules/c++/str/include/str/Manip.h @@ -23,9 +23,11 @@ #ifndef __STR_MANIP_H__ #define __STR_MANIP_H__ -#include #include + +#include #include + #include "str/Convert.h" namespace str diff --git a/externals/coda-oss/modules/c++/str/source/Manip.cpp b/externals/coda-oss/modules/c++/str/source/Manip.cpp index 7b2140256..65f6a2d94 100644 --- a/externals/coda-oss/modules/c++/str/source/Manip.cpp +++ b/externals/coda-oss/modules/c++/str/source/Manip.cpp @@ -19,15 +19,16 @@ * see . * */ +#include + +#include +#include +#include #include #include #include -#include -#include -#include - -#include +#include namespace { diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounter.h b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounter.h index 569d8454f..b193339d7 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounter.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounter.h @@ -22,14 +22,16 @@ #ifndef __SYS_ATOMIC_COUNTER_H__ #define __SYS_ATOMIC_COUNTER_H__ +#pragma once -#include +#include +#include #if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) #include #elif (defined(WIN32) || defined(_WIN32)) #include -#elif defined(__sun) && defined(HAVE_ATOMIC_H) +#elif defined(__sun) //&& defined(HAVE_ATOMIC_H) // atomic.h is available in Solaris 10+ // TODO: For Solaris 9 and older, we currently use the mutex implementation // http://blogs.oracle.com/d/entry/atomic_operations @@ -58,13 +60,13 @@ namespace sys * * TODO: Provide other operations such as getThenSet() and compareThenSet(). */ -class AtomicCounter +template +struct AtomicCounterT final { -public: - typedef AtomicCounterImpl::ValueType ValueType; + using ValueType = typename TAtomicCounterImpl::ValueType ; //! Constructor - AtomicCounter(ValueType initialValue = 0) : + AtomicCounterT(ValueType initialValue = 0) : mImpl(initialValue) { } @@ -146,14 +148,24 @@ class AtomicCounter return mImpl.get(); } -private: - // Noncopyable - AtomicCounter(const AtomicCounter& ); - const AtomicCounter& operator=(const AtomicCounter& ); + AtomicCounterT(const AtomicCounterT&) = delete; + AtomicCounterT& operator=(const AtomicCounterT&) = delete; private: - AtomicCounterImpl mImpl; + TAtomicCounterImpl mImpl; }; + +// platform-specific +using AtomicCounterOS = AtomicCounterT; +// built on +using AtomicCounterCpp11 = AtomicCounterT; + +#if !CODA_OSS_cpp17 +using AtomicCounter = AtomicCounterOS; // TODO: AtomicCounterCpp11 +#else +using AtomicCounter = AtomicCounterCpp11; +#endif + } #endif diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterCpp11.h b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterCpp11.h new file mode 100644 index 000000000..bf949f948 --- /dev/null +++ b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterCpp11.h @@ -0,0 +1,68 @@ +/* ========================================================================= + * This file is part of sys-c++ + * ========================================================================= + * + * (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_AtomicCounterCpp11_h_INCLUDED_ +#define CODA_OSS_sys_AtomicCounterCpp11_h_INCLUDED_ +#pragma once + +#include + +#include // C++11: https://en.cppreference.com/w/cpp/atomic/atomic + +namespace sys +{ +struct AtomicCounterImplCpp11 final +{ + using ValueType = size_t ; + + explicit AtomicCounterImplCpp11(ValueType initialValue) : + mValue(initialValue) + { + } + + ValueType getThenIncrement() + { + // https://en.cppreference.com/w/cpp/atomic/atomic/fetch_add + return mValue.fetch_add(1); + } + + ValueType getThenDecrement() + { + // https://en.cppreference.com/w/cpp/atomic/atomic/fetch_sub + return mValue.fetch_sub(1); + } + + ValueType get() const + { + // https://en.cppreference.com/w/cpp/atomic/atomic/load + return mValue.load(); + } + + AtomicCounterImplCpp11(const AtomicCounterImplCpp11&) = delete; + AtomicCounterImplCpp11& operator=(const AtomicCounterImplCpp11&) = delete; + +private: + std::atomic mValue; +}; +} + +#endif // CODA_OSS_sys_AtomicCounterCpp11_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterMutex.h b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterMutex.h index c8ae0ad10..c56a8f0a4 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterMutex.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterMutex.h @@ -27,9 +27,8 @@ namespace sys { -class AtomicCounterImpl +struct AtomicCounterImpl { -public: typedef size_t ValueType; explicit @@ -73,10 +72,8 @@ class AtomicCounterImpl return value; } -private: - // Noncopyable - AtomicCounterImpl(const AtomicCounterImpl& ); - const AtomicCounterImpl& operator=(const AtomicCounterImpl& ); + AtomicCounterImpl(const AtomicCounterImpl&) = delete; + AtomicCounterImpl& operator=(const AtomicCounterImpl&) = delete; private: ValueType mValue; diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterSolaris.h b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterSolaris.h index dbaafcb6e..eb406ddad 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterSolaris.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterSolaris.h @@ -30,9 +30,8 @@ namespace sys { // Implemented from boost/smart_ptr/detail/atomic_count_solaris.hpp -class AtomicCounterImpl +struct AtomicCounterImpl { -public: typedef Uint32_T ValueType; explicit @@ -56,10 +55,8 @@ class AtomicCounterImpl return static_cast(mValue); } -private: - // Noncopyable - AtomicCounterImpl(const AtomicCounterImpl& ); - const AtomicCounterImpl& operator=(const AtomicCounterImpl& ); + AtomicCounterImpl(const AtomicCounterImpl&) = delete; + AtomicCounterImpl& operator=(const AtomicCounterImpl&) = delete; private: ValueType mValue; diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterWin32.h b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterWin32.h index ce1998d52..d78a17dd6 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterWin32.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterWin32.h @@ -29,9 +29,8 @@ namespace sys { // Implemented from boost/smart_ptr/detail/atomic_count_win32.hpp -class AtomicCounterImpl +struct AtomicCounterImpl { -public: typedef long ValueType; explicit @@ -55,10 +54,8 @@ class AtomicCounterImpl return static_cast(mValue); } -private: - // Noncopyable - AtomicCounterImpl(const AtomicCounterImpl& ); - const AtomicCounterImpl& operator=(const AtomicCounterImpl& ); + AtomicCounterImpl(const AtomicCounterImpl&) = delete; + AtomicCounterImpl& operator=(const AtomicCounterImpl&) = delete; private: ValueType mValue; diff --git a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterX86.h b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterX86.h index aa2805a7b..5231e587b 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterX86.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/AtomicCounterX86.h @@ -26,9 +26,8 @@ namespace sys { // Implemented from boost/smart_ptr/detail/atomic_count_gcc_x86.hpp -class AtomicCounterImpl +struct AtomicCounterImpl { -public: typedef int ValueType; explicit @@ -52,6 +51,9 @@ class AtomicCounterImpl return atomicExchangeAndAdd(&mValue, 0); } + AtomicCounterImpl(const AtomicCounterImpl&) = delete; + AtomicCounterImpl& operator=(const AtomicCounterImpl&) = delete; + private: static ValueType atomicExchangeAndAdd(ValueType* pw, ValueType dv) @@ -74,11 +76,6 @@ class AtomicCounterImpl return r; } -private: - // Noncopyable - AtomicCounterImpl(const AtomicCounterImpl& ); - const AtomicCounterImpl& operator=(const AtomicCounterImpl& ); - private: mutable ValueType mValue; }; diff --git a/externals/coda-oss/modules/c++/sys/include/sys/Backtrace.h b/externals/coda-oss/modules/c++/sys/include/sys/Backtrace.h index d0d285895..ebfb9d2e8 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Backtrace.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Backtrace.h @@ -3,6 +3,7 @@ * ========================================================================= * * (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 @@ -20,10 +21,27 @@ * */ -#ifndef __SYS_BACKTRACE_H__ -#define __SYS_BACKTRACE_H__ +#ifndef CODA_OSS_sys_Backtrace_h_INCLUDED_ +#define CODA_OSS_sys_Backtrace_h_INCLUDED_ +#pragma once #include +#include + +// We know at compile-time whether sys::getBacktrace() is supported. +#if defined(__GNUC__) +// https://man7.org/linux/man-pages/man3/backtrace.3.html +// "These functions are GNU extensions." +#define CODA_OSS_sys_Backtrace 20210216L +#elif _WIN32 +#define CODA_OSS_sys_Backtrace 20210216L +#else +#define CODA_OSS_sys_Backtrace 0 +#endif + +namespace version { namespace sys { +constexpr auto backtrace = CODA_OSS_sys_Backtrace; +} } namespace sys { @@ -32,12 +50,12 @@ namespace sys * function calls. Usefulness and format may vary depending on * your platform and what kind of symbols are compiled in. * - * Currently only supported on *nix with glibc. This function will + * Currently only supported on *nix with glibc and Windows. This function will * return with an error message instead of a backtrace if the current * configuration is unsupported. */ -std::string getBacktrace(); +std::string getBacktrace(bool* pSupported = nullptr); +std::string getBacktrace(bool& supported, std::vector& frames); } -#endif - +#endif // CODA_OSS_sys_Backtrace_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/sys/include/sys/ConditionVarPosix.h b/externals/coda-oss/modules/c++/sys/include/sys/ConditionVarPosix.h index 889902741..b1f7c692b 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/ConditionVarPosix.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/ConditionVarPosix.h @@ -24,9 +24,9 @@ #ifndef __SYS_THREAD_PTHREAD_CONDITION_VARIABLE_H__ #define __SYS_THREAD_PTHREAD_CONDITION_VARIABLE_H__ -#include +#include -#if defined(HAVE_PTHREAD_H) +#if CODA_OSS_POSIX_SOURCE #include "sys/MutexPosix.h" #include "sys/ConditionVarInterface.h" 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 a957da98e..16d405ff6 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Conf.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Conf.h @@ -24,6 +24,15 @@ #define __SYS_CONF_H__ #pragma once +// POSIX is more-or-less "Unix" +// https://linux.die.net/man/7/feature_test_macros +// "If no feature test macros are explicitly defined, then the following feature test macros +// are defined by default: ... _POSIX_SOURCE, and _POSIX_C_SOURCE=200809L. [...] +// _POSIX_SOURCE Defining this obsolete macro ... is equivalent to defining _POSIX_C_SOURCE ..." +#define CODA_OSS_POSIX_SOURCE (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 1)) +#define CODA_OSS_POSIX2001_SOURCE CODA_OSS_POSIX_SOURCE && (_POSIX_C_SOURCE >= 200112L) +#define CODA_OSS_POSIX2008_SOURCE CODA_OSS_POSIX2001_SOURCE && (_POSIX_C_SOURCE >= 200809L) + #include #include #include @@ -106,13 +115,14 @@ namespace sys typedef HANDLE Handle_T; typedef Int64_T Off_T; typedef DWORD Pid_T; -# if SIZEOF_SIZE_T == 8 +# if _WIN64 // SIZEOF_SIZE_T == 8 + static_assert(sizeof(size_t) == 8, "wrong sizeof(size_t)"); typedef Int64_T SSize_T; -# elif SIZEOF_SIZE_T == 4 +# else // SIZEOF_SIZE_T == 4 + static_assert(sizeof(size_t) == 4, "wrong sizeof(size_t)"); typedef Int32_T SSize_T; -# else - #error SIZEOF_SIZE_T must be set at configure time # endif + static_assert(sizeof(size_t) == sizeof(SSize_T), "size_t and SSize_T should be the same size"); } #else // !windows # include @@ -314,19 +324,23 @@ namespace sys inline void* alignedAlloc(size_t size, size_t alignment = SSE_INSTRUCTION_ALIGNMENT) { -#if defined(WIN32) || defined(_WIN32) - void* p = _aligned_malloc(size, alignment); -#elif defined(HAVE_POSIX_MEMALIGN) void* p = nullptr; +#if defined(WIN32) || defined(_WIN32) + p = _aligned_malloc(size, alignment); +#elif CODA_OSS_POSIX2001_SOURCE + // https://linux.die.net/man/3/posix_memalign if (posix_memalign(&p, alignment, size) != 0) { p = nullptr; } -#elif defined(HAVE_MEMALIGN) - void* const p = memalign(alignment, size); +#elif CODA_OSS_POSIX_SOURCE + // https://linux.die.net/man/3/posix_memalign + // "The functions memalign(), ... have been available in all Linux libc libraries." + p = memalign(alignment, size); #else //! this is a basic unaligned allocation - void* p = malloc(size); + p = malloc(size); + #error "Don't know how to implement alignedAlloc()." #endif if (!p) throw except::Exception(Ctxt( diff --git a/externals/coda-oss/modules/c++/sys/include/sys/DateTime.h b/externals/coda-oss/modules/c++/sys/include/sys/DateTime.h index 1a78190c1..28232dbbf 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/DateTime.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/DateTime.h @@ -23,10 +23,12 @@ #ifndef __SYS_DATE_TIME_H__ #define __SYS_DATE_TIME_H__ +#pragma once -#include #include +#include + namespace sys { @@ -36,15 +38,15 @@ namespace sys class DateTime { protected: - int mYear; - int mMonth; - int mDayOfMonth; - int mDayOfWeek; - int mDayOfYear; - int mHour; - int mMinute; - double mSecond; - double mTimeInMillis; + int mYear = 0; + int mMonth = 0; + int mDayOfMonth = 0; + int mDayOfWeek = 0; + int mDayOfYear = 0; + int mHour = 0; + int mMinute = 0; + double mSecond = 0.0; + double mTimeInMillis = 0.0; // Turn a tm struct into a double double toMillis(tm t) const; @@ -71,9 +73,12 @@ class DateTime //! @brief Given seconds since the epoch, provides the time virtual void getTime(time_t numSecondsSinceEpoch, tm& t) const = 0; + static void localtime(time_t numSecondsSinceEpoch, tm& t); + static void gmtime(time_t numSecondsSinceEpoch, tm& t); + public: - DateTime(); - virtual ~DateTime(); + DateTime() = default; + virtual ~DateTime() {} //! Return month {1,12} int getMonth() const { return mMonth; } @@ -175,6 +180,14 @@ class DateTime //@} }; +// Always make our own versions available for unit-testing. Clients should use +// DateTme methods and implementers DateTime::localtime()/DateTime::gmtime(). +namespace details +{ +extern int localtime_s(tm*, const time_t*); +extern int gmtime_s(tm*, const time_t*); +} + } #endif//__SYS_DATE_TIME_H__ diff --git a/externals/coda-oss/modules/c++/sys/include/sys/Dbg.h b/externals/coda-oss/modules/c++/sys/include/sys/Dbg.h index 6e91ea55f..52126be87 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Dbg.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Dbg.h @@ -3,6 +3,7 @@ * ========================================================================= * * (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 @@ -21,8 +22,8 @@ */ -#ifndef __DBG_H__ -#define __DBG_H__ +#ifndef CODA_OSS_sys_Dbg_h_INCLUDED_ +#define CODA_OSS_sys_Dbg_h_INCLUDED_ #include #include @@ -35,6 +36,52 @@ # include #endif +// A "debug" build has debugging symbols, detailed call stacks, minimal optimization, STL validation, etc. +// A "release" build is likely to "run fast" and be "shipped;" it might lack much of what is in a "debug" build. +#ifndef CODA_OSS_DEBUG +#if defined(_MSC_VER) + // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-160 + #if _DEBUG // "Defined as 1 ... . Otherwise, undefined." + #ifdef NDEBUG + #error "NDEBUG #define'd with _DEBUG" + #endif + #define CODA_OSS_DEBUG 1 + #endif // _DEBUG +#elif defined(__GNUC__) + // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros + #if __OPTIMIZE__ // "... is defined ... . If they are defined, their value is 1." + #ifndef NDEBUG + //#error "NDEBUG should be #define'd with __OPTIMIZE__" + #endif + #define CODA_OSS_DEBUG 0 + #endif // __OPTIMIZE__ +#else + #error "Can't #define CODA_OSS_DEBUG for this compiler." +#endif +#endif // CODA_OSS_DEBUG +#ifndef CODA_OSS_DEBUG // not set above, check NDEBUG + #ifdef NDEBUG // https://en.cppreference.com/w/c/error/assert + #define CODA_OSS_DEBUG 0 // NDEBUG = "No DEBUG" + #else + #define CODA_OSS_DEBUG 1 + #endif // NDEBUG +#endif +#ifndef CODA_OSS_DEBUG + #error "Unable to #define CODA_OSS_DEBUG" +#endif + +// Be sure NDEBUG and CODA_OSS_DEBUG are in-sync +// GCC doesn't set NDEBUG w/__OPTIMIZE__ (see above), so we can't be too rigorous +#if CODA_OSS_DEBUG && defined(NDEBUG) + #error "Both CODA_OSS_DEBUG and NDEBUG are set." +#endif + +namespace sys +{ +constexpr auto debug_build = CODA_OSS_DEBUG ? true : false; +constexpr auto release_build = !debug_build; +} + #ifndef DEBUG_STREAM #define DEBUG_STREAM stderr #endif @@ -79,6 +126,23 @@ * open emacs, for instance, with the file in question at the line number * in question. */ +// Keep __DEBUG for existing code/scripts; but shouldn't be used. +#ifndef CODA_OSS_debugging + #ifdef __DEBUG + #define CODA_OSS_debugging 1 + // or ... use the value of CODA_OSS_DEBUG ? + //#define CODA_OSS_debugging CODA_OSS_DEBUG + #else + // or here ... ? + //#define CODA_OSS_debugging CODA_OSS_DEBUG + #define CODA_OSS_debugging 0 + #endif +#endif // CODA_OSS_debugging + +namespace sys +{ +constexpr bool debugging = CODA_OSS_debugging ? true : false; +} namespace sys { @@ -101,7 +165,7 @@ void diePrintf(const char *format, ...); #define die_printf sys::diePrintf #define dbg_ln(STR) dbg_printf("[%s, %d]: '%s'\n", __FILE__, __LINE__, STR) -#ifdef __DEBUG +#if CODA_OSS_debugging #ifndef __DEBUG_SHORTEN_EVAL #define EVAL(X) std::cout << '(' << __FILE__ << ',' <<__LINE__ << ") "#X"=" << X << std::endl @@ -121,4 +185,4 @@ void diePrintf(const char *format, ...); #define ASSERT_OR(A, E) 1 #endif -#endif // __DBG_H__ +#endif // CODA_OSS_sys_Dbg_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/sys/include/sys/Exec.h b/externals/coda-oss/modules/c++/sys/include/sys/Exec.h index 710f95086..9b8d56d56 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/Exec.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/Exec.h @@ -82,11 +82,8 @@ class Exec : public sys::Runnable * \brief opens a child process and connects a pipe * to read back the std::cout */ -class ExecPipe : Exec +struct ExecPipe : Exec { - -public: - /*! * Constructor -- * Kicks off child process and connects a pipe to the std::cout @@ -136,6 +133,9 @@ class ExecPipe : Exec // this is a blocking call until the process is complete int closePipe(); + ExecPipe(const ExecPipe&) = delete; + ExecPipe& operator=(const ExecPipe&) = delete; + protected: #ifdef _WIN32 @@ -153,12 +153,6 @@ class ExecPipe : Exec //! forcefully kill the process and call closePipe int killProcess(); - -private: - - //! Noncopyable - ExecPipe(const ExecPipe& ); - const ExecPipe& operator=(const ExecPipe& ); }; } diff --git a/externals/coda-oss/modules/c++/sys/include/sys/MutexPosix.h b/externals/coda-oss/modules/c++/sys/include/sys/MutexPosix.h index 39e623847..e954c3312 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/MutexPosix.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/MutexPosix.h @@ -24,9 +24,9 @@ #ifndef __SYS_MUTEX_POSIX_H__ #define __SYS_MUTEX_POSIX_H__ -#include +#include -#if defined(HAVE_PTHREAD_H) +#if CODA_OSS_POSIX_SOURCE #include "sys/MutexInterface.h" #include diff --git a/externals/coda-oss/modules/c++/sys/include/sys/SemaphorePosix.h b/externals/coda-oss/modules/c++/sys/include/sys/SemaphorePosix.h index 49078abc3..fc1731d8f 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/SemaphorePosix.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/SemaphorePosix.h @@ -24,9 +24,9 @@ #ifndef __SYS_SEMAPHORE_POSIX_H__ #define __SYS_SEMAPHORE_POSIX_H__ -#include +#include -#if defined(HAVE_PTHREAD_H) && !defined(__APPLE_CC__) +#if CODA_OSS_POSIX_SOURCE &&!defined(__APPLE_CC__) #include "sys/SemaphoreInterface.h" diff --git a/externals/coda-oss/modules/c++/sys/include/sys/ThreadInterface.h b/externals/coda-oss/modules/c++/sys/include/sys/ThreadInterface.h index 36f7ec0fa..9ea31adb4 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/ThreadInterface.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/ThreadInterface.h @@ -68,9 +68,8 @@ namespace sys * */ -class ThreadInterface : public Runnable +struct ThreadInterface : public Runnable { -public: enum { DEFAULT_LEVEL, KERNEL_LEVEL, USER_LEVEL }; enum { MINIMUM_PRIORITY, NORMAL_PRIORITY, MAXIMUM_PRIORITY }; @@ -242,6 +241,10 @@ class ThreadInterface : public Runnable mIsRunning = isRunning; } + + ThreadInterface(const ThreadInterface&) = delete; + ThreadInterface& operator=(const ThreadInterface&) = delete; + private: bool mIsSelf; @@ -272,10 +275,6 @@ class ThreadInterface : public Runnable //! The level at which this thread runs int mLevel; bool mIsRunning; - - // Noncopyable - ThreadInterface(const ThreadInterface& ); - const ThreadInterface& operator=(const ThreadInterface& ); }; } diff --git a/externals/coda-oss/modules/c++/sys/include/sys/ThreadPosix.h b/externals/coda-oss/modules/c++/sys/include/sys/ThreadPosix.h index 51e6d5316..bbb18ae7a 100644 --- a/externals/coda-oss/modules/c++/sys/include/sys/ThreadPosix.h +++ b/externals/coda-oss/modules/c++/sys/include/sys/ThreadPosix.h @@ -24,9 +24,9 @@ #ifndef __SYS_THREAD_PTHREAD_THREAD_H__ #define __SYS_THREAD_PTHREAD_THREAD_H__ -#include +#include -#if defined(HAVE_PTHREAD_H) +#if CODA_OSS_POSIX_SOURCE #include #include diff --git a/externals/coda-oss/modules/c++/sys/source/Backtrace.cpp b/externals/coda-oss/modules/c++/sys/source/Backtrace.cpp index 212c988f9..48a9970c4 100644 --- a/externals/coda-oss/modules/c++/sys/source/Backtrace.cpp +++ b/externals/coda-oss/modules/c++/sys/source/Backtrace.cpp @@ -22,23 +22,38 @@ #include -#include +#include #include -static const size_t MAX_STACK_ENTRIES = 62; +#include + +#if !CODA_OSS_sys_Backtrace + +static std::string getBacktrace(bool* pSupported, std::vector*) +{ + if (pSupported != nullptr) + { + *pSupported = false; + } + return "sys::getBacktrace() is not supported " + "on the current platform and/or libc"; +} + +#else -#ifdef HAVE_EXECINFO_H +#if defined(__GNUC__) +// https://man7.org/linux/man-pages/man3/backtrace.3.html +// "These functions are GNU extensions." #include -#include + +constexpr size_t MAX_STACK_ENTRIES = 62; namespace { - //! RAII wrapper for stack symbols -class BacktraceHelper +struct BacktraceHelper final { -public: BacktraceHelper(char** stackSymbols) : mStackSymbols(stackSymbols) {} @@ -48,18 +63,22 @@ class BacktraceHelper std::free(mStackSymbols); } - std::string operator[](size_t idx) + std::string operator[](size_t idx) const { return mStackSymbols[idx]; } private: char** mStackSymbols; }; - } -std::string sys::getBacktrace() +static std::string getBacktrace(bool* pSupported, std::vector* pFrames) { + if (pSupported != nullptr) + { + *pSupported = true; + } + void* stackBuffer[MAX_STACK_ENTRIES]; int currentStackSize = backtrace(stackBuffer, MAX_STACK_ENTRIES); BacktraceHelper stackSymbols(backtrace_symbols(stackBuffer, @@ -68,18 +87,83 @@ std::string sys::getBacktrace() std::stringstream ss; for (int ii = 0; ii < currentStackSize; ++ii) { - ss << stackSymbols[ii] << std::endl; + auto stackSymbol = stackSymbols[ii] + "\n"; + ss << stackSymbol; + if (pFrames != nullptr) + { + pFrames->push_back(std::move(stackSymbol)); + } } return ss.str(); } -#else +#elif _WIN32 +#include +#include +#pragma comment(lib, "dbghelp") -std::string sys::getBacktrace() +static std::string getBacktrace(bool* pSupported, std::vector* pFrames) { - return "sys::getBacktrace() is not supported " - "on the current platform and/or libc"; + if (pSupported != nullptr) + { + *pSupported = true; + } + + // https://stackoverflow.com/a/5699483/8877 + HANDLE process = GetCurrentProcess(); + auto result = SymInitialize(process, NULL, TRUE) == TRUE ? true : false; // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize + if (!result) + { + return "sys::getBacktrace(): SymInitialize() failed"; + } + + PVOID stack[100]; + auto frames = CaptureStackBackTrace(0, 100, stack, NULL); + auto symbol = reinterpret_cast(calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1)); + if (symbol == nullptr) + { + return "sys::getBacktrace(): calloc() failed"; + } + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + std::string retval; + for (unsigned int i = 0; i < frames; i++) + { + const auto address = reinterpret_cast(stack[i]); + result = SymFromAddr(process, address, 0, symbol) == TRUE ? true : false; + if (!result) + { + continue; + } + auto frame = str::format("%i: %s - 0x%0X\n", + frames - i - 1, + symbol->Name, + symbol->Address); + retval += frame; + if (pFrames != nullptr) + { + pFrames->push_back(std::move(frame)); + } + } + + free(symbol); + return retval; } +#else + +#error "CODA_OSS_sys_Backtrace inconsistency." + #endif +#endif // CODA_OSS_sys_Backtrace + +std::string sys::getBacktrace(bool* pSupported) +{ + return ::getBacktrace(pSupported, nullptr /*frames*/); +} +std::string sys::getBacktrace(bool& supported, std::vector& frames) +{ + return ::getBacktrace(&supported, &frames); +} diff --git a/externals/coda-oss/modules/c++/sys/source/ConditionVarPosix.cpp b/externals/coda-oss/modules/c++/sys/source/ConditionVarPosix.cpp index 77a62b7b2..144c25bee 100644 --- a/externals/coda-oss/modules/c++/sys/source/ConditionVarPosix.cpp +++ b/externals/coda-oss/modules/c++/sys/source/ConditionVarPosix.cpp @@ -22,7 +22,7 @@ #include -#if defined(HAVE_PTHREAD_H) +#if CODA_OSS_POSIX_SOURCE #include diff --git a/externals/coda-oss/modules/c++/sys/source/DateTime.cpp b/externals/coda-oss/modules/c++/sys/source/DateTime.cpp index 2346b2091..eb3059dca 100644 --- a/externals/coda-oss/modules/c++/sys/source/DateTime.cpp +++ b/externals/coda-oss/modules/c++/sys/source/DateTime.cpp @@ -19,20 +19,23 @@ * see . * */ +#include "sys/DateTime.h" -#include +#include +#include +#include +#include + +#include #include "except/Exception.h" -#include "sys/DateTime.h" #include "sys/Conf.h" #include "str/Convert.h" #include "str/Manip.h" -#include -#include -#if defined(HAVE_SYS_TIME_H) +#if CODA_OSS_POSIX_SOURCE #include -#elif defined(_WIN32) +#elif _WIN32 #include #endif @@ -379,17 +382,20 @@ double sys::DateTime::toMillis(tm t) const return (timeInSeconds + timediff) * 1000; } -void sys::DateTime::setNow() +static double getNowInMillis() { -#ifdef HAVE_CLOCK_GETTIME + // https://linux.die.net/man/2/gettimeofday + // "SVr4, 4.3BSD. POSIX.1-2001 describes gettimeofday() ... POSIX.1-2008 marks + // gettimeofday() as obsolete, recommending the use of clock_gettime(2) instead." +#if CODA_OSS_POSIX2008_SOURCE struct timespec now; clock_gettime(CLOCK_REALTIME,&now); - mTimeInMillis = (now.tv_sec + 1.0e-9 * now.tv_nsec) * 1000; -#elif defined(HAVE_SYS_TIME_H) + return (now.tv_sec + 1.0e-9 * now.tv_nsec) * 1000; +#elif CODA_OSS_POSIX_SOURCE struct timeval now; gettimeofday(&now,NULL); - mTimeInMillis = (now.tv_sec + 1.0e-6 * now.tv_usec) * 1000; -#elif defined(_WIN32) + return (now.tv_sec + 1.0e-6 * now.tv_usec) * 1000; +#elif _WIN32 // Getting time twice may be inefficient but is quicker // than converting the SYSTEMTIME structure into // milliseconds @@ -397,10 +403,14 @@ void sys::DateTime::setNow() // does not need millisecond accuracy SYSTEMTIME now; GetLocalTime(&now); - mTimeInMillis = (double)time(NULL) * 1000 + now.wMilliseconds; + return (double)time(NULL) * 1000 + now.wMilliseconds; #else - mTimeInMillis = (double)time(NULL) * 1000; + return (double)time(NULL) * 1000; #endif +} +void sys::DateTime::setNow() +{ + mTimeInMillis = getNowInMillis(); fromMillis(); } @@ -409,21 +419,6 @@ void sys::DateTime::getTime(tm& t) const getTime(static_cast(mTimeInMillis / 1000), t); } -sys::DateTime::DateTime() : - mYear(0), - mMonth(0), - mDayOfMonth(0), - mDayOfWeek(0), - mDayOfYear(0), - mHour(0), - mMinute(0), - mSecond(0.0), - mTimeInMillis(0.0) -{ } - -sys::DateTime::~DateTime() -{} - std::string sys::DateTime::monthToString(int month) { switch (month) @@ -601,3 +596,89 @@ std::string sys::DateTime::format(const std::string& formatStr) const return std::string(str); } + +// https://en.cppreference.com/w/c/chrono/localtime +// "The structure may be shared between gmtime, localtime, and ctime ... ." +static std::mutex g_dateTimeMutex; +template +static inline int time_s(F f, tm* t, const time_t* numSecondsSinceEpoch) +{ + tm* result = nullptr; + { + std::lock_guard guard(g_dateTimeMutex); + result = f(numSecondsSinceEpoch); + } + if (result == nullptr) + { + return errno; + } + + *t = *result; + return 0; // no error +} +int sys::details::localtime_s(tm* t, const time_t* numSecondsSinceEpoch) +{ + return time_s(localtime, t, numSecondsSinceEpoch); +} +int sys::details::gmtime_s(tm* t, const time_t* numSecondsSinceEpoch) +{ + return time_s(gmtime, t, numSecondsSinceEpoch); +} + +void sys::DateTime::localtime(time_t numSecondsSinceEpoch, tm& t) +{ + // Would like to use the reentrant version. If we don't have one, cross + // our fingers and hope the regular function actually is reentrant + // (supposedly this is the case on Windows). +#if CODA_OSS_POSIX_SOURCE + if (::localtime_r(&numSecondsSinceEpoch, &t) == NULL) + { + int const errnum = errno; + throw except::Exception(Ctxt("localtime_r() failed (" + + std::string(::strerror(errnum)) + ")")); + } +#elif _WIN32 + const auto errnum = ::localtime_s(&t, &numSecondsSinceEpoch); + if (errnum != 0) + { + throw except::Exception(Ctxt("localtime_s() failed (" + + std::string(::strerror(errnum)) + ")")); + } +#else + const auto errnum = sys::details::localtime_s(&t, &numSecondsSinceEpoch); + if (errnum != 0) + { + throw except::Exception(Ctxt("localtime failed (" + + std::string(::strerror(errnum)) + ")")); + } +#endif +} + +void sys::DateTime::gmtime(time_t numSecondsSinceEpoch, tm& t) +{ + // Would like to use the reentrant version. If we don't have one, cross + // our fingers and hope the regular function actually is reentrant + // (supposedly this is the case on Windows). +#if CODA_OSS_POSIX_SOURCE + if (::gmtime_r(&numSecondsSinceEpoch, &t) == NULL) + { + int const errnum = errno; + throw except::Exception(Ctxt("gmtime_r() failed (" + + std::string(::strerror(errnum)) + ")")); + } +#elif _WIN32 + const auto errnum = ::gmtime_s(&t, &numSecondsSinceEpoch); + if (errnum != 0) + { + throw except::Exception(Ctxt("gmtime_s() failed (" + + std::string(::strerror(errnum)) + ")")); + } +#else + const auto errnum = sys::details::gmtime_s(&t, &numSecondsSinceEpoch); + if (errnum != 0) + { + throw except::Exception(Ctxt("gmtime failed (" + + std::string(::strerror(errnum)) + ")")); + } +#endif +} \ No newline at end of file diff --git a/externals/coda-oss/modules/c++/sys/source/Dbg.cpp b/externals/coda-oss/modules/c++/sys/source/Dbg.cpp index bdd2bb24a..e2d573eb3 100644 --- a/externals/coda-oss/modules/c++/sys/source/Dbg.cpp +++ b/externals/coda-oss/modules/c++/sys/source/Dbg.cpp @@ -3,6 +3,7 @@ * ========================================================================= * * (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 @@ -25,15 +26,14 @@ void sys::dbgPrintf(const char *format, ...) { -#ifdef __DEBUG - va_list args; - va_start(args, format); - fprintf(DEBUG_STREAM, " "); - vfprintf(DEBUG_STREAM, format, args); - va_end(args); -#else - (void)format; //suppress unreferenced formal parameter warning when __DEBUG not defined -#endif + if (sys::debugging) + { + va_list args; + va_start(args, format); + fprintf(DEBUG_STREAM, " "); + vfprintf(DEBUG_STREAM, format, args); + va_end(args); + } } void sys::diePrintf(const char *format, ...) diff --git a/externals/coda-oss/modules/c++/sys/source/LocalDateTime.cpp b/externals/coda-oss/modules/c++/sys/source/LocalDateTime.cpp index c3fca4b63..22bde3d57 100755 --- a/externals/coda-oss/modules/c++/sys/source/LocalDateTime.cpp +++ b/externals/coda-oss/modules/c++/sys/source/LocalDateTime.cpp @@ -19,11 +19,6 @@ * see . * */ - -#include - -#include - #include #include @@ -59,33 +54,7 @@ void LocalDateTime::toMillis() void LocalDateTime::getTime(time_t numSecondsSinceEpoch, tm& t) const { - // Would like to use the reentrant version. If we don't have one, cross - // our fingers and hope the regular function actually is reentrant - // (supposedly this is the case on Windows). -#ifdef _WIN32 - const auto errnum = ::localtime_s(&t, &numSecondsSinceEpoch); - if (errnum != 0) - { - throw except::Exception(Ctxt("localtime_s() failed (" + - std::string(::strerror(errnum)) + ")")); - } -#elif defined(HAVE_LOCALTIME_R) - if (::localtime_r(&numSecondsSinceEpoch, &t) == NULL) - { - int const errnum = errno; - throw except::Exception(Ctxt("localtime_r() failed (" + - std::string(::strerror(errnum)) + ")")); - } -#else - tm const * const localTimePtr = ::localtime(&numSecondsSinceEpoch); - if (localTimePtr == NULL) - { - int const errnum = errno; - throw except::Exception(Ctxt("localtime failed (" + - std::string(::strerror(errnum)) + ")")); - } - t = *localTimePtr; -#endif + DateTime::localtime(numSecondsSinceEpoch, t); } LocalDateTime::LocalDateTime() : diff --git a/externals/coda-oss/modules/c++/sys/source/MutexPosix.cpp b/externals/coda-oss/modules/c++/sys/source/MutexPosix.cpp index 04b9882e9..f28ce93be 100644 --- a/externals/coda-oss/modules/c++/sys/source/MutexPosix.cpp +++ b/externals/coda-oss/modules/c++/sys/source/MutexPosix.cpp @@ -22,7 +22,7 @@ #include -#if defined(HAVE_PTHREAD_H) +#if CODA_OSS_POSIX_SOURCE diff --git a/externals/coda-oss/modules/c++/sys/source/OSUnix.cpp b/externals/coda-oss/modules/c++/sys/source/OSUnix.cpp index 992f86010..e91b7a88d 100644 --- a/externals/coda-oss/modules/c++/sys/source/OSUnix.cpp +++ b/externals/coda-oss/modules/c++/sys/source/OSUnix.cpp @@ -20,19 +20,21 @@ * */ -#include "config/coda_oss_config.h" - -#if !(defined(WIN32) || defined(_WIN32)) - #include #include -#include -#include #include +#include + +#include #include #include #include -#include + +#include "sys/Conf.h" + +#if !(defined(WIN32) || defined(_WIN32)) + +#include #if defined(__APPLE__) @@ -314,7 +316,8 @@ void sys::OSUnix::setEnv(const std::string& var, { int ret; -#ifdef HAVE_SETENV +#if CODA_OSS_POSIX2001_SOURCE + // https://man7.org/linux/man-pages/man3/setenv.3.html ret = setenv(var.c_str(), val.c_str(), overwrite); #else // putenv() will overwrite the value if it already exists, so if we don't diff --git a/externals/coda-oss/modules/c++/sys/source/SemaphorePosix.cpp b/externals/coda-oss/modules/c++/sys/source/SemaphorePosix.cpp index 605326cf5..0d6d09cee 100644 --- a/externals/coda-oss/modules/c++/sys/source/SemaphorePosix.cpp +++ b/externals/coda-oss/modules/c++/sys/source/SemaphorePosix.cpp @@ -22,7 +22,7 @@ #include -#if defined(HAVE_PTHREAD_H) && !defined(__APPLE_CC__) +#if CODA_OSS_POSIX_SOURCE && !defined(__APPLE_CC__) #include diff --git a/externals/coda-oss/modules/c++/sys/source/ThreadPosix.cpp b/externals/coda-oss/modules/c++/sys/source/ThreadPosix.cpp index d7601a555..07f7f9867 100644 --- a/externals/coda-oss/modules/c++/sys/source/ThreadPosix.cpp +++ b/externals/coda-oss/modules/c++/sys/source/ThreadPosix.cpp @@ -22,7 +22,7 @@ #include -#if defined(HAVE_PTHREAD_H) +#if CODA_OSS_POSIX_SOURCE #if defined(WIN32) || defined(_WIN32) # define SIGKILL 0 diff --git a/externals/coda-oss/modules/c++/sys/source/UTCDateTime.cpp b/externals/coda-oss/modules/c++/sys/source/UTCDateTime.cpp index 45b532b4b..0a6e8fd08 100755 --- a/externals/coda-oss/modules/c++/sys/source/UTCDateTime.cpp +++ b/externals/coda-oss/modules/c++/sys/source/UTCDateTime.cpp @@ -19,11 +19,8 @@ * see . * */ - -#include - -#include #include + #include #include #include @@ -133,33 +130,7 @@ void UTCDateTime::toMillis() void UTCDateTime::getTime(time_t numSecondsSinceEpoch, tm& t) const { - // Would like to use the reentrant version. If we don't have one, cross - // our fingers and hope the regular function actually is reentrant - // (supposedly this is the case on Windows). -#if _WIN32 - const auto errnum = ::gmtime_s(&t, &numSecondsSinceEpoch); - if (errnum != 0) - { - throw except::Exception(Ctxt("gmtime_s() failed (" + - std::string(::strerror(errnum)) + ")")); - } -#elif defined(HAVE_GMTIME_R) - if (::gmtime_r(&numSecondsSinceEpoch, &t) == NULL) - { - int const errnum = errno; - throw except::Exception(Ctxt("gmtime_r() failed (" + - std::string(::strerror(errnum)) + ")")); - } -#else - tm const * const gmTimePtr = ::gmtime(&numSecondsSinceEpoch); - if (gmTimePtr == NULL) - { - int const errnum = errno; - throw except::Exception(Ctxt("gmtime failed (" + - std::string(::strerror(errnum)) + ")")); - } - t = *gmTimePtr; -#endif + DateTime::gmtime(numSecondsSinceEpoch, t); } UTCDateTime::UTCDateTime() diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_atomic_counter.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_atomic_counter.cpp index d329d65e1..f3ebafea1 100644 --- a/externals/coda-oss/modules/c++/sys/unittests/test_atomic_counter.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_atomic_counter.cpp @@ -32,21 +32,31 @@ namespace { -typedef sys::AtomicCounter::ValueType ValueType; - -TEST_CASE(testConstructor) +template +static void testConstructor_(const std::string& testName) { - TEST_ASSERT_EQ(sys::AtomicCounter().get(), 0); - TEST_ASSERT_EQ(sys::AtomicCounter(12345).get(), 12345); + using ValueType = typename TAtomicCounter::ValueType; + + TEST_ASSERT_EQ(TAtomicCounter().get(), 0); + TEST_ASSERT_EQ(TAtomicCounter(12345).get(), 12345); TEST_ASSERT_EQ( - sys::AtomicCounter(std::numeric_limits::max()).get(), + TAtomicCounter(std::numeric_limits::max()).get(), std::numeric_limits::max()); } +TEST_CASE(testConstructor) +{ + testConstructor_(testName); + testConstructor_(testName); + testConstructor_(testName); +} -TEST_CASE(testIncrement) +template +static void testIncrement_(const std::string& testName) { - sys::AtomicCounter ctr(100); + using ValueType = typename TAtomicCounter::ValueType; + + TAtomicCounter ctr(100); TEST_ASSERT_EQ(ctr.getThenIncrement(), 100); TEST_ASSERT_EQ(ctr.get(), 101); @@ -65,10 +75,19 @@ TEST_CASE(testIncrement) TEST_ASSERT_EQ(value, 105); TEST_ASSERT_EQ(ctr.get(), 105); } +TEST_CASE(testIncrement) +{ + testIncrement_(testName); + testIncrement_(testName); + testIncrement_(testName); +} -TEST_CASE(testDecrement) +template +static void testDecrement_(const std::string& testName) { - sys::AtomicCounter ctr(100); + using ValueType = typename TAtomicCounter::ValueType; + + TAtomicCounter ctr(100); TEST_ASSERT_EQ(ctr.getThenDecrement(), 100); TEST_ASSERT_EQ(ctr.get(), 99); @@ -87,12 +106,18 @@ TEST_CASE(testDecrement) TEST_ASSERT_EQ(value, 95); TEST_ASSERT_EQ(ctr.get(), 95); } +TEST_CASE(testDecrement) +{ + testDecrement_(testName); + testDecrement_(testName); + testDecrement_(testName); +} -class IncrementAtomicCounter : public sys::Runnable +template +struct IncrementAtomicCounterT final : public sys::Runnable { -public: - IncrementAtomicCounter(size_t numIncrements, - sys::AtomicCounter& ctr, + IncrementAtomicCounterT(size_t numIncrements, + TAtomicCounter& ctr, ValueType* values) : mNumIncrements(numIncrements), mCtr(ctr), @@ -110,19 +135,23 @@ class IncrementAtomicCounter : public sys::Runnable private: const size_t mNumIncrements; - sys::AtomicCounter& mCtr; + TAtomicCounter& mCtr; ValueType* const mValues; }; -TEST_CASE(testThreadedIncrement) +template +static void testThreadedIncrement_(const std::string& testName) { + using ValueType = typename TAtomicCounter::ValueType; + using IncrementAtomicCounter = IncrementAtomicCounterT; + const size_t numThreads = 13; const size_t numIncrements = 1000; std::vector values(numThreads * numIncrements); std::vector valuesPtr(numThreads); std::vector threads(numThreads); - sys::AtomicCounter ctr(0); + TAtomicCounter ctr(0); // Create all the threads ValueType* ptr(&values[0]); @@ -161,15 +190,21 @@ TEST_CASE(testThreadedIncrement) std::sort(values.begin(), values.end()); for (size_t ii = 0; ii < values.size(); ++ii) { - TEST_ASSERT_EQ(values[ii], (sys::SSize_T)ii); + TEST_ASSERT_EQ(static_cast(values[ii]), static_cast(ii)); } } +TEST_CASE(testThreadedIncrement) +{ + testThreadedIncrement_(testName); + testThreadedIncrement_(testName); + testThreadedIncrement_(testName); +} -class DecrementAtomicCounter : public sys::Runnable +template +struct DecrementAtomicCounterT final : public sys::Runnable { -public: - DecrementAtomicCounter(size_t numDecrements, - sys::AtomicCounter& ctr, + DecrementAtomicCounterT(size_t numDecrements, + TAtomicCounter& ctr, ValueType* values) : mNumDecrements(numDecrements), mCtr(ctr), @@ -187,19 +222,23 @@ class DecrementAtomicCounter : public sys::Runnable private: const size_t mNumDecrements; - sys::AtomicCounter& mCtr; + TAtomicCounter& mCtr; ValueType* const mValues; }; -TEST_CASE(testThreadedDecrement) +template +static void testThreadedDecrement_(const std::string& testName) { + using ValueType = typename TAtomicCounter::ValueType; + using DecrementAtomicCounter = DecrementAtomicCounterT; + const size_t numThreads = 13; const size_t numDecrements = 1000; std::vector values(numThreads * numDecrements); std::vector valuesPtr(numThreads); std::vector threads(numThreads); - sys::AtomicCounter ctr(numThreads * numDecrements - 1); + TAtomicCounter ctr(numThreads * numDecrements - 1); // Create all the threads ValueType* ptr(&values[0]); @@ -238,9 +277,15 @@ TEST_CASE(testThreadedDecrement) std::sort(values.begin(), values.end()); for (size_t ii = 0; ii < values.size(); ++ii) { - TEST_ASSERT_EQ(values[ii], (sys::SSize_T)ii); + TEST_ASSERT_EQ(static_cast(values[ii]), static_cast(ii)); } } +TEST_CASE(testThreadedDecrement) +{ + testThreadedDecrement_(testName); + testThreadedDecrement_(testName); + testThreadedDecrement_(testName); +} } int main(int, char**) diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_datetime.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_datetime.cpp index 8d2cdad54..c6c5e9443 100755 --- a/externals/coda-oss/modules/c++/sys/unittests/test_datetime.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_datetime.cpp @@ -157,12 +157,58 @@ TEST_CASE(testParameterizedConstructor) TEST_ASSERT_EQ(u5.getSecond(), u4.getSecond()); } +static void testDateTimeDetails_(const std::string& testName, const tm& result, const sys::DateTime& dt) +{ + const auto ad = result.tm_year + 1900; // "years since 1900" + // this might break in 2038: https://en.wikipedia.org/wiki/Year_2038_problem + TEST_ASSERT_GREATER_EQ(ad, 1900); + TEST_ASSERT_GREATER_EQ(ad, 1970); + TEST_ASSERT_GREATER_EQ(ad, 2021); + TEST_ASSERT_LESSER_EQ(result.tm_yday, 365); // "days since January 1" + + TEST_ASSERT_EQ(ad, dt.getYear()); + TEST_ASSERT_EQ(result.tm_yday + 1, dt.getDayOfYear()); +} +TEST_CASE(testDateTimeDetails) +{ + const time_t now = time(nullptr); + { + tm local; + const auto result = sys::details::localtime_s(&local, &now); + TEST_ASSERT_EQ(0, result); + testDateTimeDetails_(testName, local, sys::LocalDateTime()); + } + { + tm global; + const auto result = sys::details::gmtime_s(&global, &now); + TEST_ASSERT_EQ(0, result); + testDateTimeDetails_(testName, global, sys::UTCDateTime()); + } +} + +TEST_CASE(testGetTimeInMillis) +{ + const sys::LocalDateTime lt; + const auto result = lt.getTimeInMillis(); + TEST_ASSERT_GREATER_EQ(result, 0.0); + constexpr auto February_02_2021 = 1612928129.0 * 1000.0; // in milliseconds + TEST_ASSERT_GREATER_EQ(result, February_02_2021); + + constexpr auto recent_past = February_02_2021 * 0.999; + TEST_ASSERT_GREATER_EQ(result, recent_past); + + constexpr auto far_into_the_future = February_02_2021 * 100.0; + TEST_ASSERT_LESSER_EQ(result, far_into_the_future); +} + } int main(int, char**) { TEST_CHECK(testDefaultConstructor); TEST_CHECK(testParameterizedConstructor); + TEST_CHECK(testDateTimeDetails); + TEST_CHECK(testGetTimeInMillis); return 0; } diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp index eac17d158..1583250ef 100644 --- a/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_os.cpp @@ -20,12 +20,17 @@ * */ +#include + #include #include +#include // std::accumulate #include #include #include +#include +#include #include "TestCase.h" namespace @@ -219,6 +224,59 @@ TEST_CASE(testFsOutput) #endif } +static std::string f(bool& supported, std::vector& frames) +{ + return sys::getBacktrace(supported, frames); +} +static std::string g(bool& supported, std::vector& frames) +{ + return f(supported, frames); +} +static std::string h(bool& supported, std::vector& frames) +{ + return g(supported, frames); +} +TEST_CASE(testBacktrace) +{ + bool supported; + std::vector frames; + const auto result = h(supported, frames); + TEST_ASSERT_TRUE(!result.empty()); + + size_t frames_size = 0; + auto version_sys_backtrace_ = version::sys::backtrace; // "Conditional expression is constant" + if (version_sys_backtrace_ >= 20210216L) + { + TEST_ASSERT_TRUE(supported); + + #if _WIN32 + constexpr auto frames_size_RELEASE = 2; + constexpr auto frames_size_DEBUG = 13; + #elif defined(__GNUC__) + constexpr auto frames_size_RELEASE = 5; + constexpr auto frames_size_DEBUG = 9; + #else + #error "CODA_OSS_sys_Backtrace inconsistency." + #endif + frames_size = sys::debug_build ? frames_size_DEBUG : frames_size_RELEASE; + } + else + { + TEST_ASSERT_FALSE(supported); + } + TEST_ASSERT_EQ(frames.size(), frames_size); + + const auto msg = std::accumulate(frames.begin(), frames.end(), std::string()); + if (supported) + { + TEST_ASSERT_EQ(result, msg); + } + else + { + TEST_ASSERT_TRUE(msg.empty()); + } +} + } int main(int, char**) @@ -228,6 +286,7 @@ int main(int, char**) TEST_CHECK(testEnvVariables); TEST_CHECK(testFsExtension); TEST_CHECK(testFsOutput); + TEST_CHECK(testBacktrace); return 0; } diff --git a/externals/coda-oss/modules/c++/tiff/include/tiff/FileWriter.h b/externals/coda-oss/modules/c++/tiff/include/tiff/FileWriter.h index 6276d81b7..fb495a676 100644 --- a/externals/coda-oss/modules/c++/tiff/include/tiff/FileWriter.h +++ b/externals/coda-oss/modules/c++/tiff/include/tiff/FileWriter.h @@ -41,10 +41,8 @@ namespace tiff * to the same file. Contains function for manipulating each * sub-image and for writing data. *********************************************************************/ -class FileWriter +struct FileWriter { -public: - //! Constructor FileWriter() : mIFDOffset(0) @@ -116,11 +114,8 @@ class FileWriter *****************************************************************/ void writeHeader(); - -private: - // Noncopyable - FileWriter(const FileWriter& ); - const FileWriter& operator=(const FileWriter& ); + FileWriter(const FileWriter&) = delete; + FileWriter& operator=(const FileWriter&) = delete; private: //! The position to write the offset to the first IFD to diff --git a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ContentHandler.h b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ContentHandler.h index ff72f6725..e086bc847 100644 --- a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ContentHandler.h +++ b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/ContentHandler.h @@ -25,8 +25,13 @@ #pragma once #include +#include #include +#include + +#include "sys/CPlusPlus.h" + #include "xml/lite/Attributes.h" /*! @@ -39,6 +44,29 @@ * suit your needs. */ +// If `wchar_t` is built-in (as it should be), then `f(wchar_t)` can be overloaded +// with `f(uint16_t)` and/or `f(uint32_t)`. If it is **not** (old "C++11" compilers) +// then one of those overload attempts will fail. +#ifndef CODA_OSS_wchar_t_is_type_ + #if defined(_MSC_VER) + // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-160 + // "Defined as 1 when the /Zc:wchar_t compiler option is set. Otherwise, undefined." + #define CODA_OSS_wchar_t_is_type_ (_NATIVE_WCHAR_T_DEFINED == 1) + #else + #if CODA_OSS_cpp14 + #define CODA_OSS_wchar_t_is_type_ 1 + #else + // If your "wchar_t" isn't a distinct type, set this to 0. + // On old systems, it might be a typedef for uint16_t or uint32_t + //#define CODA_OSS_wchar_t_is_type_ 0 + #define CODA_OSS_wchar_t_is_type_ 1 + #endif // CODA_OSS_cpp14 + #endif +#endif +// https://docs.microsoft.com/en-us/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type?view=msvc-160 +// "... the C++ standard requires that wchar_t be a built-in type. " +static_assert(CODA_OSS_wchar_t_is_type_, "wchar_t should be a built-in type."); + namespace xml { namespace lite @@ -93,7 +121,13 @@ class ContentHandler * \param length The length of the new data */ virtual void characters(const char *data, int length) = 0; - virtual bool characters(const wchar_t* const /*data*/, const size_t /*length*/) + #if CODA_OSS_wchar_t_is_type_ + virtual bool characters(const wchar_t* /*data*/, size_t /*length*/) + { return false; /* continue on to existing characters()*/ } /* =0 would break existing code */ + #endif + virtual bool characters(const uint16_t* /*data*/, size_t /*length*/) + { return false; /* continue on to existing characters()*/ } /* =0 would break existing code */ + virtual bool characters(const uint32_t* /*data*/, size_t /*length*/) { return false; /* continue on to existing characters()*/ } /* =0 would break existing code */ virtual bool use_wchar_t() const // =0 would break existing code diff --git a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/MinidomHandler.h b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/MinidomHandler.h index 2d8ffffd0..c1613e056 100644 --- a/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/MinidomHandler.h +++ b/externals/coda-oss/modules/c++/xml.lite/include/xml/lite/MinidomHandler.h @@ -108,7 +108,11 @@ class MinidomHandler : public ContentHandler * \param length The length of the char data */ virtual void characters(const char* value, int length) override; - bool characters(const wchar_t* const value, const size_t length) override; + #if CODA_OSS_wchar_t_is_type_ + bool characters(const wchar_t* value, size_t length) override; + #endif + bool characters(const uint16_t* /*data*/, size_t /*length*/) override; + bool characters(const uint32_t* /*data*/, size_t /*length*/) override; // Which characters() routine should be called? bool use_wchar_t() const override; @@ -188,6 +192,11 @@ class MinidomHandler : public ContentHandler bool mOwnDocument; bool mPreserveCharData; bool mStoreEncoding = false; + + private: + template + bool characters_(const T* value, size_t length); + bool call_characters(const std::string& utf8Value); }; } } diff --git a/externals/coda-oss/modules/c++/xml.lite/source/MinidomHandler.cpp b/externals/coda-oss/modules/c++/xml.lite/source/MinidomHandler.cpp index 3d4b2d3a2..78478438b 100644 --- a/externals/coda-oss/modules/c++/xml.lite/source/MinidomHandler.cpp +++ b/externals/coda-oss/modules/c++/xml.lite/source/MinidomHandler.cpp @@ -21,6 +21,7 @@ */ #include +#include #include "str/Manip.h" #include "str/Convert.h" @@ -87,22 +88,55 @@ void xml::lite::MinidomHandler::characters(const char *value, int length) #endif characters(value, length, pEncoding); } -bool xml::lite::MinidomHandler::characters(const wchar_t* const value_, const size_t length_) + +template +inline std::string toUtf8_(const CharT* value, size_t length) { - #ifndef _WIN32 - // As on Windows, this comes to us already encoded ... but UTF-32 - const auto value = reinterpret_cast(value_); - const std::u32string strValue(value, length_); + const std::basic_string strValue(value, length); std::string utf8Value; str::toUtf8(strValue, utf8Value); + return utf8Value; +} +inline std::string toUtf8(const uint16_t* value_, size_t length) +{ + const auto value = reinterpret_cast(value_); + return toUtf8_(value, length); +} +inline std::string toUtf8(const uint32_t* value_, size_t length) +{ + const auto value = reinterpret_cast(value_); + return toUtf8_(value, length); +} +#if CODA_OSS_wchar_t_is_type_ +inline std::string toUtf8(const wchar_t* value_, size_t length) +{ + using wchar_t_type = std::conditional::type; +#ifdef _WIN32 + // if we somehow get here on Windows (shouldn't, see below), wchar_t is UTF-16 not UTF-32 + static_assert(sizeof(wchar_t) == sizeof(wchar_t_type), "wchar_t should be 16-bits on Windows."); +#endif + const auto value = reinterpret_cast(value_); + return toUtf8(value, length); +} +#endif +bool xml::lite::MinidomHandler::call_characters(const std::string& utf8Value) +{ const auto length = static_cast(utf8Value.length()); - static const auto encoding = string_encoding::utf_8; + static const auto encoding = xml::lite::string_encoding::utf_8; characters(utf8Value.c_str(), length, &encoding); - return true; // all done, characters(char*) already called, above + return true; // all done, characters(char*) already called, above +} + +template +bool xml::lite::MinidomHandler::characters_(const T* value, size_t length) +{ + #ifndef _WIN32 + const auto utf8Value = toUtf8(value, length); + return call_characters(utf8Value); // all done, characters(char*) already called, above #else - UNREFERENCED_PARAMETER(value_); - UNREFERENCED_PARAMETER(length_); + UNREFERENCED_PARAMETER(value); + UNREFERENCED_PARAMETER(length); // On Windows, we want std::string encoded as Windows-1252 (ISO8859-1) // so that western European characters will be displayed. We can't convert // to UTF-8 (as above on Linux), because Windows doesn't have good support @@ -111,6 +145,20 @@ bool xml::lite::MinidomHandler::characters(const wchar_t* const value_, const si return false; // call characters(char*) to get a Windows-1252 string #endif } +#if CODA_OSS_wchar_t_is_type_ +bool xml::lite::MinidomHandler::characters(const wchar_t* value, size_t length) +{ + return characters_(value, length); +} +#endif +bool xml::lite::MinidomHandler::characters(const uint32_t* value, size_t length) +{ + return characters_(value, length); +} +bool xml::lite::MinidomHandler::characters(const uint16_t* value, size_t length) +{ + return characters_(value, length); +} bool xml::lite::MinidomHandler::use_wchar_t() const { diff --git a/externals/coda-oss/modules/c++/zip/source/ZipFile.cpp b/externals/coda-oss/modules/c++/zip/source/ZipFile.cpp index 50ca5fe24..1256d5cf4 100644 --- a/externals/coda-oss/modules/c++/zip/source/ZipFile.cpp +++ b/externals/coda-oss/modules/c++/zip/source/ZipFile.cpp @@ -121,10 +121,10 @@ void ZipFile::readCentralDir() readCentralDirValues(eocd, (mCompressed + mCompressedLength) - eocd); p = mCompressed + mCentralDirOffset; - sys::SSize_T len = (mCompressed + mCompressedLength) - p; - for (size_t i = 0; i < mEntries.size(); ++i) + const sys::SSize_T len = (mCompressed + mCompressedLength) - p; + for (auto& entry : mEntries) { - mEntries[i] = newCentralDirEntry(&p, len); + entry = newCentralDirEntry(&p, len); } } diff --git a/externals/nitro/modules/c++/nitf/include/nitf/TRE.hpp b/externals/nitro/modules/c++/nitf/include/nitf/TRE.hpp index 4f3fedd76..48d4dfaff 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/TRE.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/TRE.hpp @@ -40,7 +40,7 @@ namespace nitf * \class FieldIterator * \brief The C++ wrapper for the nitf_TREEnumerator */ - struct TREFieldIterator /*final*/ : public nitf::Object // no "final", SWIG doesn't like it +struct TREFieldIterator : public nitf::Object // no "final", SWIG doesn't like it { TREFieldIterator() noexcept(false) { diff --git a/externals/nitro/modules/c++/nitf/source/CompressionInterface.cpp b/externals/nitro/modules/c++/nitf/source/CompressionInterface.cpp index 77a7bf056..18ef6b8ef 100644 --- a/externals/nitro/modules/c++/nitf/source/CompressionInterface.cpp +++ b/externals/nitro/modules/c++/nitf/source/CompressionInterface.cpp @@ -63,7 +63,7 @@ NITF_BOOL CompressionInterface::adapterStart( NITF_BOOL CompressionInterface::adapterWriteBlock( nitf_CompressionControl* object, nitf_IOInterface* io, - const std::byte* data, + const uint8_t* data, NITF_BOOL pad, NITF_BOOL noData, nitf_Error* error) @@ -97,6 +97,17 @@ NITF_BOOL CompressionInterface::adapterWriteBlock( return NRT_FAILURE; } } +NITF_BOOL CompressionInterface::adapterWriteBlock( + nitf_CompressionControl* object, + nitf_IOInterface* io, + const std::byte* data_, + NITF_BOOL pad, + NITF_BOOL noData, + nitf_Error* error) +{ + const auto data = reinterpret_cast(data_); + return adapterWriteBlock(object, io, data, pad, noData, error); +} NITF_BOOL CompressionInterface::adapterEnd( nitf_CompressionControl* object, diff --git a/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp b/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp index 3ebab0798..939488334 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp @@ -90,23 +90,35 @@ const std::string output_file = "test_writer_3++.nitf"; namespace fs = std::filesystem; static std::string argv0; + +static bool is_vs_gtest() +{ + return argv0.empty(); // no argv[0] in VS w/GTest +} + static fs::path findInputFile(const std::string& name) { - const fs::path inputFile = fs::path("modules") / "c++" / "nitf" / "unittests" / name; + const auto inputFile = fs::path("modules") / "c++" / "nitf" / "unittests" / name; - fs::path root; - if (argv0.empty()) + if (is_vs_gtest()) // running Google Test in Visual Studio { - // running in Visual Studio - root = fs::current_path().parent_path().parent_path(); - } - else - { - root = fs::absolute(argv0).parent_path().parent_path().parent_path().parent_path(); - root = root.parent_path().parent_path(); + const auto root= fs::current_path().parent_path().parent_path(); + return root / inputFile; } - return root / inputFile; + const auto exe = fs::absolute(argv0); + fs::path root = exe.parent_path(); + do + { + auto retval = root / inputFile; + if (fs::exists(retval)) + { + return retval; + } + root = root.parent_path(); + } while (!root.empty()); + + return inputFile; } TEST_CASE(test_nitf_Record_unmergeTREs_crash) diff --git a/externals/nitro/modules/c/CMakeLists.txt b/externals/nitro/modules/c/CMakeLists.txt index b953c3431..15dc3dbbe 100644 --- a/externals/nitro/modules/c/CMakeLists.txt +++ b/externals/nitro/modules/c/CMakeLists.txt @@ -4,5 +4,9 @@ set(TARGET_LANGUAGE c) add_subdirectory(nrt) add_subdirectory(nitf) add_subdirectory(cgm) -add_subdirectory(j2k) + +if (ENABLE_J2K) + add_subdirectory(j2k) +endif() + add_subdirectory(jpeg) diff --git a/externals/nitro/modules/c/cgm/CMakeLists.txt b/externals/nitro/modules/c/cgm/CMakeLists.txt index 2675a0dee..3a8e9674a 100644 --- a/externals/nitro/modules/c/cgm/CMakeLists.txt +++ b/externals/nitro/modules/c/cgm/CMakeLists.txt @@ -1,5 +1,12 @@ set(MODULE_NAME cgm) +if (BUILD_SHARED) + set(BUILD_SHARED_LIBS ON) + add_definitions( + -DNITF_MODULE_EXPORTS + ) +endif() + coda_add_module( ${MODULE_NAME} DEPS nitf-c diff --git a/externals/nitro/modules/c/j2k/CMakeLists.txt b/externals/nitro/modules/c/j2k/CMakeLists.txt index a0ab1bdf2..41d18abb7 100644 --- a/externals/nitro/modules/c/j2k/CMakeLists.txt +++ b/externals/nitro/modules/c/j2k/CMakeLists.txt @@ -4,6 +4,13 @@ if (TARGET openjpeg) set(HAVE_OPENJPEG_H 1) endif() +if (BUILD_SHARED) + set(BUILD_SHARED_LIBS ON) + add_definitions( + -DJ2K_MODULE_EXPORTS + ) +endif() + coda_generate_module_config_header(${MODULE_NAME}) coda_add_module( diff --git a/externals/nitro/modules/c/jpeg/CMakeLists.txt b/externals/nitro/modules/c/jpeg/CMakeLists.txt index 830d184fc..cc493c80f 100644 --- a/externals/nitro/modules/c/jpeg/CMakeLists.txt +++ b/externals/nitro/modules/c/jpeg/CMakeLists.txt @@ -1,3 +1,11 @@ + +if (BUILD_SHARED) + set(BUILD_SHARED_LIBS ON) + add_definitions( + -DNITF_MODULE_EXPORTS + ) +endif() + if (TARGET jpeg) # refers to libjpeg (external dependency) coda_add_module( jpeg # becomes jpeg-c (nitro module) diff --git a/externals/nitro/modules/c/nitf/CMakeLists.txt b/externals/nitro/modules/c/nitf/CMakeLists.txt index 8e8e1506a..ec5f6956c 100644 --- a/externals/nitro/modules/c/nitf/CMakeLists.txt +++ b/externals/nitro/modules/c/nitf/CMakeLists.txt @@ -2,6 +2,13 @@ set(MODULE_NAME nitf) coda_generate_module_config_header(${MODULE_NAME}) +if (BUILD_SHARED) + set(BUILD_SHARED_LIBS ON) + add_definitions( + -DNITF_MODULE_EXPORTS + ) +endif() + coda_add_module( ${MODULE_NAME} DEPS nrt-c @@ -72,7 +79,8 @@ coda_add_tests( test_image_io.c test_mem_source.c test_tre_mods.c - test_zero_field.c) + test_zero_field.c + test_moveTREs.c) # Build all the TRE diff --git a/externals/nitro/modules/c/nitf/source/NitfWriter.c b/externals/nitro/modules/c/nitf/source/NitfWriter.c index 5dc5d5ba2..b33e682a4 100644 --- a/externals/nitro/modules/c/nitf/source/NitfWriter.c +++ b/externals/nitro/modules/c/nitf/source/NitfWriter.c @@ -834,6 +834,13 @@ NITFAPI(NITF_BOOL) nitf_Writer_prepareIO(nitf_Writer* writer, NITF_ERR_MEMORY); return NITF_FAILURE; } + + /* first, set the writer to NULL */ + for (i = 0; i < numImages; i++) + { + writer->imageWriters[i] = NULL; + } + writer->numImageWriters = numImages; for (i = 0; i < numImages; i++) { @@ -842,9 +849,6 @@ NITFAPI(NITF_BOOL) nitf_Writer_prepareIO(nitf_Writer* writer, uint32_t nbpp, nbands, xbands, nrows, ncols; uint64_t length; - /* first, set the writer to NULL */ - writer->imageWriters[i] = NULL; - /* guard against an overflowing data length */ iter = nitf_List_at(record->images, i); segment = (nitf_ImageSegment*) nitf_ListIterator_get(&iter); diff --git a/externals/nitro/modules/c/nitf/source/Record.c b/externals/nitro/modules/c/nitf/source/Record.c index cce8ffa82..04967aa5b 100644 --- a/externals/nitro/modules/c/nitf/source/Record.c +++ b/externals/nitro/modules/c/nitf/source/Record.c @@ -20,6 +20,8 @@ * */ +#include + #include "nitf/Record.h" /* @@ -271,6 +273,7 @@ moveTREs(nitf_Extensions* source, { tre = nitf_ExtensionsIterator_get(&srcIter); treLength = (uint32_t)tre->handler->getCurrentSize(tre, error); + treLength += NITF_ETAG_SZ + NITF_EL_SZ; skipLeft -= treLength; if (skipLeft < 1) break; @@ -2033,139 +2036,152 @@ nitf_Record_moveReservedExtensionSegment(nitf_Record* record, * securityCls - security class field (i.e., imageSecurityClass) * securityGrp - Security group object (i.e., securityGroup) * idx - Index field (DE index in original segment) (i.e.,UDOFL) - * typeStr - Type string (i.e.,UDID) + * segmentType - Type string (i.e.,UDID) */ - -#define UNMERGE_SEGMENT(section, securityCls, securityGrp, idx, typeStr) \ - length = nitf_Extensions_computeLength(section, version, error); \ - if (length > maxLength) \ - { \ - if (!nitf_Field_get( \ - idx, &overflowIndex, NITF_CONV_INT, NITF_INT32_SZ, error)) \ - { \ - nitf_Error_init(error, \ - "Could not retrieve overflow segment index", \ - NITF_CTXT, \ - NITF_ERR_INVALID_OBJECT); \ - return NITF_FAILURE; \ - } \ - if (overflowIndex == 0) \ - { \ - overflowIndex = addOverflowSegment(record, \ - segIndex, \ - #typeStr, \ - securityCls, \ - securityGrp, \ - &overflow, \ - error); \ - if (overflowIndex == 0) \ - { \ - nitf_Error_init(error, \ - "Could not add overflow segment", \ - NITF_CTXT, \ - NITF_ERR_INVALID_OBJECT); \ - return NITF_FAILURE; \ - } \ - } \ - if ((overflow == NULL) || !moveTREs(section, \ - overflow->subheader->userDefinedSection, \ - maxLength, \ - error)) \ - { \ - nitf_Error_init(error, \ - "Could not transfer TREs to overflow segment", \ - NITF_CTXT, \ - NITF_ERR_INVALID_OBJECT); \ - return NITF_FAILURE; \ - } \ - if (!nitf_Field_setUint32(idx, overflowIndex, error)) \ - { \ - nitf_Error_init(error, \ - "Could not set overflow segment index", \ - NITF_CTXT, \ - NITF_ERR_INVALID_OBJECT); \ - return NITF_FAILURE; \ - } \ - } - -NITFAPI(NITF_BOOL) -nitf_Record_unmergeTREs(nitf_Record* record, nitf_Error* error) +NITFPRIV(NITF_BOOL) unmergeSegment(nitf_Version version, nitf_Record* record, + nitf_Extensions* section, nitf_Field* securityCls, nitf_FileSecurity* securityGrp, nitf_Field* idx, char* segmentType, + uint32_t maxLength, uint32_t segIndex, nitf_Error* error) { - /* NITF version */ - nitf_Version version; + assert(record != NULL); + assert(section != NULL); - /* File header */ - nitf_FileHeader* header; + /* Overflow segment */ + nitf_DESegment* overflow = NULL; - /* Current segment list */ - nitf_ListIterator segIter; + /* Length of TREs in current section */ + const uint32_t length = nitf_Extensions_computeLength(section,version,error); + if (length > maxLength) + { + /* Overflow index of current extension */ + uint32_t overflowIndex; + if (!nitf_Field_get(idx, &overflowIndex, + NITF_CONV_INT,NITF_INT32_SZ, error)) + { + nitf_Error_init(error, "Could not retrieve overflow segment index", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } + if (overflowIndex == 0) + { + overflowIndex = addOverflowSegment(record, segIndex, segmentType, + securityCls, securityGrp, &overflow, error); + if (overflowIndex == 0) + { + nitf_Error_init(error, "Could not add overflow segment", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } - /* Current segment list end */ - nitf_ListIterator segEnd; + if (!nitf_Field_setUint32(idx, overflowIndex, error)) + { + nitf_Error_init(error, "Could not set overflow segment index", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } + } + else /* already tested for 0 above, wrap-around from -1 (below) isn't possible */ + { + assert(overflowIndex > 0); + nitf_ListIterator iter = nitf_List_at(record->dataExtensions, overflowIndex - 1); + const nitf_ListIterator end = nitf_List_end(record->dataExtensions); + if (nitf_ListIterator_notEqualTo(&iter, &end)) + { + overflow = (nitf_DESegment*)nitf_ListIterator_get(&iter); + } + } - /* Current segment index */ - uint32_t segIndex; + if (overflow == NULL) + { + nitf_Error_init(error, "Invalid dataExtension segment number", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } - /* Length of TREs in current section */ - uint32_t length; + nitf_DESubheader* subheader = overflow->subheader; + if (subheader == NULL) + { + nitf_Error_init(error, "Invalid dataExtension segment number (overflow->subheader)", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } + nitf_Extensions* userDefinedSection = subheader->userDefinedSection; + if (userDefinedSection == NULL) + { + nitf_Error_init(error, "Invalid dataExtension segment number (subheader->userDefinedSection)", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } + if(!moveTREs(section, userDefinedSection, maxLength, error)) + { + nitf_Error_init(error, "Could not transfer TREs to overflow segment", + NITF_CTXT, NITF_ERR_INVALID_OBJECT); + return NITF_FAILURE; + } + } + return NITF_SUCCESS; +} - /* Max length for this type of section */ - uint32_t maxLength; +NITFAPI(NITF_BOOL) +nitf_Record_unmergeTREs(nitf_Record* record, nitf_Error* error) +{ + /* check for NULL data */ + if (record == NULL) + { + nitf_Error_init(error, "NULL data", + NITF_CTXT, NITF_ERR_INVALID_PARAMETER); + return NITF_FAILURE; + } - /* Overflow index of current extension */ - uint32_t overflowIndex; + /* NITF version */ + const nitf_Version version = nitf_Record_getVersion(record); - /* Overflow segment */ - nitf_DESegment* overflow = NULL; + /* Max length for this type of section */ + uint32_t maxLength = 99999; - version = nitf_Record_getVersion(record); + /* Current segment index */ + uint32_t segIndex = 1; /* ??? I moved this up so this would be initialized!! */ /* File header */ + const nitf_FileHeader* header = record->header; + if (header != NULL) + { + unmergeSegment(version, record, header->userDefinedSection, + header->classification, header->securityGroup, + header->NITF_UDHOFL, "UDHD", + maxLength, segIndex, error); - maxLength = 99999; - segIndex = 1; /* ??? I moved this up so this would be initialized!! */ - - header = record->header; - UNMERGE_SEGMENT(header->userDefinedSection, - header->classification, - header->securityGroup, - header->NITF_UDHOFL, - UDHD); - - UNMERGE_SEGMENT(header->extendedSection, - header->classification, - header->securityGroup, - header->NITF_XHDLOFL, - XHD); + unmergeSegment(version, record, header->extendedSection, + header->classification, header->securityGroup, + header->NITF_XHDLOFL, "XHD", + maxLength, segIndex, error); + } /* Image segments */ - segIter = nitf_List_begin(record->images); - segEnd = nitf_List_end(record->images); + nitf_ListIterator segIter = nitf_List_begin(record->images); + nitf_ListIterator segEnd = nitf_List_end(record->images); while (nitf_ListIterator_notEqualTo(&segIter, &segEnd)) { - /* Current subheader */ - nitf_ImageSubheader* subheader; - maxLength = 99999; - - subheader = + + /* Current subheader */ + nitf_ImageSubheader* subheader = (nitf_ImageSubheader*)((nitf_ImageSegment*) nitf_ListIterator_get(&segIter)) ->subheader; - - /* User defined section */ - UNMERGE_SEGMENT(subheader->userDefinedSection, - subheader->imageSecurityClass, - subheader->securityGroup, - subheader->NITF_UDOFL, - UDID); - - /* Extension section */ - UNMERGE_SEGMENT(subheader->extendedSection, - subheader->imageSecurityClass, - subheader->securityGroup, - subheader->NITF_IXSOFL, - IXSHD); + if (subheader != NULL) + { + /* User defined section */ + unmergeSegment(version, record, subheader->userDefinedSection, + subheader->imageSecurityClass, subheader->securityGroup, + subheader->NITF_UDOFL, "UDID", + maxLength, segIndex, error); + + /* Extension section */ + unmergeSegment(version, record, subheader->extendedSection, + subheader->imageSecurityClass, subheader->securityGroup, + subheader->NITF_IXSOFL, "IXSHD", + maxLength, segIndex, error); + } segIndex += 1; nitf_ListIterator_increment(&segIter); @@ -2178,23 +2194,22 @@ nitf_Record_unmergeTREs(nitf_Record* record, nitf_Error* error) while (nitf_ListIterator_notEqualTo(&segIter, &segEnd)) { - /* Current subheader */ - nitf_GraphicSubheader* subheader; - /* Hello, really? Somebody needs to fix hardcode DP */ maxLength = 9741; - subheader = (nitf_GraphicSubheader*)((nitf_GraphicSegment*) + /* Current subheader */ + nitf_GraphicSubheader* subheader = (nitf_GraphicSubheader*)((nitf_GraphicSegment*) nitf_ListIterator_get( &segIter)) ->subheader; - - /* Extension section */ - UNMERGE_SEGMENT(subheader->extendedSection, - subheader->securityClass, - subheader->securityGroup, - subheader->NITF_SXSOFL, - SXSHD); + if (subheader != NULL) + { + /* Extension section */ + unmergeSegment(version, record, subheader->extendedSection, + subheader->securityClass, subheader->securityGroup, + subheader->NITF_SXSOFL, "SXSHD", + maxLength, segIndex, error); + } segIndex += 1; nitf_ListIterator_increment(&segIter); @@ -2207,24 +2222,22 @@ nitf_Record_unmergeTREs(nitf_Record* record, nitf_Error* error) while (nitf_ListIterator_notEqualTo(&segIter, &segEnd)) { - /* Current subheader */ - nitf_LabelSubheader* subheader; - /* Fix hardcode! */ maxLength = 9747; - subheader = + /* Current subheader */ + nitf_LabelSubheader* subheader = (nitf_LabelSubheader*)((nitf_LabelSegment*) nitf_ListIterator_get(&segIter)) ->subheader; - - /* Extension section */ - - UNMERGE_SEGMENT(subheader->extendedSection, - subheader->securityClass, - subheader->securityGroup, - subheader->NITF_LXSOFL, - LXSHD); + if (subheader != NULL) + { + /* Extension section */ + unmergeSegment(version, record, subheader->extendedSection, + subheader->securityClass, subheader->securityGroup, + subheader->NITF_LXSOFL, "LXSHD", + maxLength, segIndex, error); + } segIndex += 1; nitf_ListIterator_increment(&segIter); @@ -2237,23 +2250,22 @@ nitf_Record_unmergeTREs(nitf_Record* record, nitf_Error* error) while (nitf_ListIterator_notEqualTo(&segIter, &segEnd)) { - /* Current subheader */ - nitf_TextSubheader* subheader; - /* Fix hardcode! */ maxLength = 9717; - subheader = + /* Current subheader */ + nitf_TextSubheader* subheader = (nitf_TextSubheader*)((nitf_TextSegment*)nitf_ListIterator_get( &segIter)) ->subheader; - - /* Extension section */ - UNMERGE_SEGMENT(subheader->extendedSection, - subheader->securityClass, - subheader->securityGroup, - subheader->NITF_TXSOFL, - TXSHD); + if (subheader != NULL) + { + /* Extension section */ + unmergeSegment(version, record, subheader->extendedSection, + subheader->securityClass, subheader->securityGroup, + subheader->NITF_TXSOFL, "TXSHD", + maxLength, segIndex, error); + } segIndex += 1; nitf_ListIterator_increment(&segIter); @@ -2306,8 +2318,7 @@ NITFAPI(NITF_BOOL) nitf_Record_mergeTREs(nitf_Record* record, nitf_Error* error) NITF_DESTAG_SZ + 1, error)) { - nitf_Error_init(error, - "Could not retrieve DE segment id", + nitf_Error_init(error, "Could not retrieve DE segment id", NITF_CTXT, NITF_ERR_INVALID_OBJECT); return NITF_FAILURE; @@ -2334,9 +2345,7 @@ NITFAPI(NITF_BOOL) nitf_Record_mergeTREs(nitf_Record* record, nitf_Error* error) error); if (eflag) { - nitf_Error_init( - error, - "Could not retrieve DE segment header overflow value", + nitf_Error_init(error, "Could not retrieve DE segment header overflow value", NITF_CTXT, NITF_ERR_INVALID_OBJECT); return NITF_FAILURE; diff --git a/externals/nitro/modules/c/nitf/unittests/test_moveTREs.c b/externals/nitro/modules/c/nitf/unittests/test_moveTREs.c new file mode 100644 index 000000000..3df64f2ac --- /dev/null +++ b/externals/nitro/modules/c/nitf/unittests/test_moveTREs.c @@ -0,0 +1,129 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * + * NITRO 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, If not, + * see . + * + */ + + +/** + * This test serves as an example to show how one can construct and write + * a NITF from scratch. + */ + + +#include +#include "Test.h" + +static const char* const DATE_TIME = "20120126000000"; + +static +NITF_BOOL insertTRE(nitf_Extensions* ext, nitf_Error* error) +{ + char buffer[1024]; + + memset(buffer, 0, 1024); + nitf_TRE* tre = nitf_TRE_construct("MTXFIL", "MTXFIL", error); + + nitf_TRE_setField(tre, "raw_data", (NITF_DATA*)buffer, 1024, error); + if (!nitf_Extensions_appendTRE(ext, tre, error)) + return NITF_FAILURE; + + return NITF_SUCCESS; +} + +static +NITF_BOOL initializeTREs(nitf_FileHeader* header, nitf_Error* error) +{ + int i = 0; + char buffer[1024]; + + memset(buffer, 0, 1024); + + /* Create a large number of TREs that will force a TRE_OVERFLOW DES */ + for (i = 0; i < 100; ++i) + { + insertTRE(header->extendedSection, error); + } + + return NITF_SUCCESS; +} + +static +NITF_BOOL initializeHeader(nitf_FileHeader* header, nitf_Error* error) +{ + return (nitf_Field_setString(header->fileHeader, "NITF", error) && + nitf_Field_setString(header->fileVersion, "02.10", error) && + nitf_Field_setUint32(header->complianceLevel, 3, error) && + nitf_Field_setString(header->systemType, "BF01", error) && + nitf_Field_setString(header->originStationID, "mdaus", error) && + nitf_Field_setString(header->fileDateTime, DATE_TIME, error) && + nitf_Field_setString(header->fileTitle, "TRE Overflow Test", error) && + nitf_Field_setString(header->classification, "U", error) && + nitf_Field_setUint32(header->encrypted, 0, error) && + initializeTREs(header, error)); +} + + +NITF_BOOL unmergeTREs(nitf_Record *record, nitf_Error *error) +{ + if (!nitf_Record_unmergeTREs(record, error)) + goto CATCH_ERROR; + + return NITF_SUCCESS; + + CATCH_ERROR: + return NITF_FAILURE; +} + + +TEST_CASE_ARGS(testUnmerge) +{ + nitf_Version version = NITF_VER_21; + nitf_Record *record = NULL; + nitf_Error error; + nitf_Uint32 treSize; + + TEST_ASSERT((record = nitf_Record_construct(version, &error))); + TEST_ASSERT(initializeHeader(record->header, &error)); + + TEST_ASSERT(unmergeTREs(record, &error)); + + /* Extension Segment should be less then 99999 now after the unmerge */ + treSize = nitf_Extensions_computeLength(record->header->extendedSection, version, &error); + TEST_ASSERT(treSize < 99999); + + /* Add another TRE */ + TEST_ASSERT(insertTRE(record->header->extendedSection, &error)); + + /* Unmerge again, this should move the new TRE to the existing TRE_OVERFLOW */ + TEST_ASSERT(unmergeTREs(record, &error)); + + treSize = nitf_Extensions_computeLength(record->header->extendedSection, version, &error); + TEST_ASSERT(treSize < 99999); + + + nitf_Record_destruct(&record); +} + +int main(int argc, char **argv) +{ + CHECK_ARGS(testUnmerge); + return 0; +} + diff --git a/externals/nitro/modules/c/nrt/CMakeLists.txt b/externals/nitro/modules/c/nrt/CMakeLists.txt index 6470aedca..9f6598342 100644 --- a/externals/nitro/modules/c/nrt/CMakeLists.txt +++ b/externals/nitro/modules/c/nrt/CMakeLists.txt @@ -2,6 +2,13 @@ set(MODULE_NAME nrt) coda_generate_module_config_header(${MODULE_NAME}) +if (BUILD_SHARED) + set(BUILD_SHARED_LIBS ON) + add_definitions( + -DNRT_MODULE_EXPORTS + ) +endif() + coda_add_module( ${MODULE_NAME} DEPS ${CMAKE_DL_LIBS} config-c++