diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6cf6bd1b2..76b0a81e5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,10 @@
cmake_minimum_required(VERSION 3.14)
project(nitro)
-set(CMAKE_CXX_STANDARD 14)
-set(CXX_STANDARD_REQUIRED true)
-
if (${CMAKE_PROJECT_NAME} STREQUAL nitro)
# we are the top-level project and are responsible for configuration
+ set(CMAKE_CXX_STANDARD 14)
+ set(CXX_STANDARD_REQUIRED true)
# Always turn on "warnings as errors" to avoid lots of (meaningless?) build output;
# we'll dial-back warnings as necessary.
diff --git a/externals/coda-oss/.github/workflows/build_unittest.yml b/externals/coda-oss/.github/workflows/build_unittest.yml
index 3e0cb263a..b6415715e 100644
--- a/externals/coda-oss/.github/workflows/build_unittest.yml
+++ b/externals/coda-oss/.github/workflows/build_unittest.yml
@@ -1,6 +1,14 @@
+env:
+ CTEST_TIMEOUT: 120
+
name: build_unittest
-on: [push]
+on:
+ push:
+ pull_request:
+ branches:
+ - main
+
jobs:
build-cmake-windows:
@@ -11,6 +19,8 @@ jobs:
name: ${{ matrix.os }}-${{ matrix.python-version }}-CMake
runs-on: ${{ matrix.os }}
steps:
+ - name: Skip Duplicate Actions
+ uses: fkirc/skip-duplicate-actions@v5.3.1
- uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout
- name: Set up Python
uses: actions/setup-python@v5 # https://github.com/marketplace/actions/setup-python
@@ -39,10 +49,10 @@ jobs:
- name: test # should run w/o install
run: |
cd target-Release
- ctest -C Release --output-on-failure
+ ctest -C Release --output-on-failure --timeout $env:CTEST_TIMEOUT
cd ..
cd target-Debug
- ctest -C Debug --output-on-failure
+ ctest -C Debug --output-on-failure --timeout $env:CTEST_TIMEOUT
- name: install
run: |
cd target-Release
@@ -63,6 +73,8 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
+ - name: Skip Duplicate Actions
+ uses: fkirc/skip-duplicate-actions@v5.3.1
- uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout
- name: configure
run: |
@@ -93,11 +105,13 @@ jobs:
build-linux-cmake-default:
strategy:
matrix:
- os: [ubuntu-latest]
+ os: [ubuntu-22.04]
python-version: ['3.7']
name: ${{ matrix.os }}-${{ matrix.python-version }}-CMake
runs-on: ${{ matrix.os }}
steps:
+ - name: Skip Duplicate Actions
+ uses: fkirc/skip-duplicate-actions@v5.3.1
- uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout
- name: Set up Python
uses: actions/setup-python@v5 # https://github.com/marketplace/actions/setup-python
@@ -121,7 +135,7 @@ jobs:
# should run w/o install
run: |
cd target
- ctest --output-on-failure
+ ctest --output-on-failure --timeout $CTEST_TIMEOUT
- name: install
run: |
cd target
@@ -130,50 +144,56 @@ jobs:
build-linux-cmake:
strategy:
matrix:
- os: [ubuntu-latest]
+ os: [ubuntu-22.04]
configuration: [Debug, Release]
avx: [AVX512F]
name: ${{ matrix.os }}-${{ matrix.configuration }}-${{ matrix.avx }}-CMake
runs-on: ${{ matrix.os }}
steps:
+ - name: Skip Duplicate Actions
+ uses: fkirc/skip-duplicate-actions@v5.3.1
- uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout
- name: configure
run: |
mkdir out && cd out
- cmake .. -DENABLE_PYTHON=OFF -DENABLE_${{ matrix.avx }}=ON
+ cmake .. -DENABLE_PYTHON=OFF -DENABLE_${{ matrix.avx }}=ON -DCMAKE_INSTALL_PREFIX=${{ matrix.configuration }}
- name: build
run: |
cd out
# "-j" spawns too many processes causing GCC to crash
- cmake --build . --config ${{ matrix.configuration }} -j 12
+ cmake --build . -j 12
- name: test
# should run w/o install
run: |
cd out
- ctest -C ${{ matrix.configuration }} --output-on-failure
+ ctest --output-on-failure --timeout $CTEST_TIMEOUT
build-waf:
strategy:
matrix:
- os: [ubuntu-latest, windows-2019]
+ os: [
+ # BROKEN: windows-latest,
+ ubuntu-22.04]
python-version: ['3.7']
debugging: ['--enable-debugging', '']
name: ${{ matrix.os }}-${{ matrix.python-version }}-waf${{ matrix.debugging }}
runs-on: ${{ matrix.os }}
steps:
+ - name: Skip Duplicate Actions
+ uses: fkirc/skip-duplicate-actions@v5.3.1
- uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout
- name: Set up Python
uses: actions/setup-python@v5 # https://github.com/marketplace/actions/setup-python
with:
python-version: ${{ matrix.python-version }}
- name: configure_with_swig
- if: ${{ matrix.os == 'ubuntu-latest' }}
+ if: ${{ matrix.os == 'ubuntu-22.04' }}
run: |
pip install numpy
mkdir install${{ matrix.os }}Waf-Github
python waf configure --prefix="$PWD/install${{ matrix.os }}Waf-Github" --enable-swig ${{ matrix.debugging }}
- name: configure_without_swig
- if: ${{ matrix.os == 'windows-2019' }}
+ if: ${{ matrix.os == 'windows-latest' }}
run: |
pip install numpy
mkdir install${{ matrix.os }}Waf-Github
diff --git a/externals/coda-oss/CMakeLists.txt b/externals/coda-oss/CMakeLists.txt
index f512d520a..55f1ef94a 100644
--- a/externals/coda-oss/CMakeLists.txt
+++ b/externals/coda-oss/CMakeLists.txt
@@ -3,10 +3,7 @@
# Author: Scott A. Colcord
cmake_minimum_required(VERSION 3.14)
-project(coda-oss)
-
-set(CMAKE_CXX_STANDARD 14)
-set(CXX_STANDARD_REQUIRED true)
+project(coda-oss VERSION 1.0.0)
if (EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
# build and package with conan
@@ -19,6 +16,10 @@ endif()
if (${CMAKE_PROJECT_NAME} STREQUAL coda-oss)
# this is the top level project
+ # Allow other project to set different standard.
+ set(CMAKE_CXX_STANDARD 14)
+ set(CXX_STANDARD_REQUIRED true)
+
# Always turn on "warnings as errors" to avoid lots of (meaningless?) build output;
# we'll dial-back warnings as necessary.
if (MSVC)
@@ -37,6 +38,13 @@ if (${CMAKE_PROJECT_NAME} STREQUAL coda-oss)
install(DIRECTORY "cmake/"
DESTINATION "${CODA_STD_PROJECT_LIB_DIR}/cmake/"
FILES_MATCHING PATTERN "*.cmake")
+
+ # If the package version is defined, write a version file for find_package
+ set(version_file "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}ConfigVersion.cmake")
+ include(CMakePackageConfigHelpers)
+ write_basic_package_version_file(${version_file} VERSION ${CMAKE_PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
+ install(FILES ${version_file} DESTINATION "lib/cmake")
+
endif()
add_subdirectory("modules")
diff --git a/externals/coda-oss/README.md b/externals/coda-oss/README.md
index f5792fd50..ff05d8d3e 100644
--- a/externals/coda-oss/README.md
+++ b/externals/coda-oss/README.md
@@ -11,6 +11,8 @@
CODA is a set of modules, and each module, while complimentary to one another, has
a very specific and largely independent purpose.
+CODA follows [Semantic Versioning](https://semver.org/).
+
Building CODA
--------------
diff --git a/externals/coda-oss/build/build.py b/externals/coda-oss/build/build.py
index 01d512e98..bdd34caa9 100644
--- a/externals/coda-oss/build/build.py
+++ b/externals/coda-oss/build/build.py
@@ -1271,7 +1271,7 @@ def configure(self):
env['install_libdir'] = Options.options.libdir if Options.options.libdir else join(Options.options.prefix, 'lib')
env['install_bindir'] = Options.options.bindir if Options.options.bindir else join(Options.options.prefix, 'bin')
env['install_sharedir'] = Options.options.sharedir if Options.options.sharedir else join(Options.options.prefix, 'share')
- env['install_pydir'] = Options.options.pydir if Options.options.pydir else '${PYTHONDIR}'
+ env['install_pydir'] = Options.options.pydir if Options.options.pydir else join(Options.options.prefix, 'lib/python/site-packages')
# Swig memory leak output
if Options.options.swig_silent_leak:
@@ -1526,7 +1526,12 @@ def copytree_tgt(tsk):
symlinks = tsk.symlinks
if hasattr(tsk, 'ignore'):
ignore = tsk.ignore
- shutil.copytree(tsk.src, dest, symlinks, ignore)
+
+ def copytree_helper(src, dst):
+ Logs.pprint('GREEN', f'- copy {dst} (from {src})')
+ shutil.copy2(src, dst)
+
+ shutil.copytree(tsk.src, dest, symlinks, ignore, copy_function=copytree_helper)
@task_gen
@feature('install_as_tgt')
diff --git a/externals/coda-oss/cmake/CodaBuild.cmake b/externals/coda-oss/cmake/CodaBuild.cmake
index a74502f3c..9f57b5a7e 100644
--- a/externals/coda-oss/cmake/CodaBuild.cmake
+++ b/externals/coda-oss/cmake/CodaBuild.cmake
@@ -275,14 +275,8 @@ function(coda_generate_package_config)
"cmake/${CMAKE_PROJECT_NAME}Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "lib/cmake"
- PATH_VARS ${ARGN}
- )
- #write_basic_package_version_file(
- # ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
- # VERSION 1.2.3
- # COMPATIBILITY SameMajorVersion )
+ PATH_VARS ${ARGN})
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake"
- #"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "lib/cmake")
endif()
endfunction()
@@ -314,25 +308,46 @@ function(coda_fetch_driver)
message(FATAL_ERROR "received unexpected argument(s): ${ARG_UNPARSED_ARGUMENTS}")
endif()
- set(target_name ${CMAKE_PROJECT_NAME}_${ARG_NAME})
- # Use 'FetchContent' to download and unpack the files. Set it up here.
- FetchContent_Declare(${target_name}
- URL "${CMAKE_CURRENT_SOURCE_DIR}/${ARG_ARCHIVE}"
- URL_HASH ${ARG_HASH}
- )
- FetchContent_GetProperties(${target_name})
- # The returned properties use the lower-cased name
- string(TOLOWER ${target_name} target_name_lc)
- if (NOT ${target_name_lc}_POPULATED) # This makes sure we only fetch once.
- message("Populating content for external dependency ${driver_name}")
- # Now (at configure time) unpack the content.
- FetchContent_Populate(${target_name})
- # Remember where we put stuff
- set("${target_name_lc}_SOURCE_DIR" "${${target_name_lc}_SOURCE_DIR}"
- CACHE INTERNAL "source directory for ${target_name_lc}")
- set("${target_name_lc}_BINARY_DIR" "${${target_name_lc}_BINARY_DIR}"
- CACHE INTERNAL "binary directory for ${target_name_lc}")
- endif()
+ # Use a new policy context so we can set some policy values that prevent
+ # warning messages from printing
+ cmake_policy(PUSH)
+ if (POLICY CMP0135)
+ # Newer cmake versions (>=3.24) change how the timestamps of
+ # extracted files are set. The new behavior is fine but a warning
+ # is printed if we do not either explicitly set the policy or set
+ # the minimum cmake version.
+ cmake_policy(SET CMP0135 NEW)
+ endif()
+ if (POLICY CMP0169)
+ # Newer cmake versions (>=3.30) have deprecated
+ # FetchContent_Populate and suggest using a different pattern.
+ # The _Populate version calls add_directory() which we do not want,
+ # so we cannot use it. Until we find a different solution, just
+ # ignore this warning.
+ cmake_policy(SET CMP0169 OLD)
+ endif()
+
+ set(target_name ${CMAKE_PROJECT_NAME}_${ARG_NAME})
+ # Use 'FetchContent' to download and unpack the files. Set it up here.
+ FetchContent_Declare(${target_name}
+ URL "${CMAKE_CURRENT_SOURCE_DIR}/${ARG_ARCHIVE}"
+ URL_HASH ${ARG_HASH}
+ )
+ FetchContent_GetProperties(${target_name})
+ # The returned properties use the lower-cased name
+ string(TOLOWER ${target_name} target_name_lc)
+ if (NOT ${target_name_lc}_POPULATED) # This makes sure we only fetch once.
+ message("Populating content for external dependency ${driver_name}")
+ # Now (at configure time) unpack the content.
+ FetchContent_Populate(${target_name})
+ # Remember where we put stuff
+ set("${target_name_lc}_SOURCE_DIR" "${${target_name_lc}_SOURCE_DIR}"
+ CACHE INTERNAL "source directory for ${target_name_lc}")
+ set("${target_name_lc}_BINARY_DIR" "${${target_name_lc}_BINARY_DIR}"
+ CACHE INTERNAL "binary directory for ${target_name_lc}")
+ endif()
+ cmake_policy(POP)
+
endfunction()
diff --git a/externals/coda-oss/modules/c++/CMakeLists.txt b/externals/coda-oss/modules/c++/CMakeLists.txt
index f8e98accb..9db23eb77 100644
--- a/externals/coda-oss/modules/c++/CMakeLists.txt
+++ b/externals/coda-oss/modules/c++/CMakeLists.txt
@@ -80,3 +80,6 @@ add_subdirectory("hdf5.lite")
add_subdirectory("gsl")
add_subdirectory("std")
+if(ENABLE_NLOHMANN)
+ add_subdirectory("coda_oss.json")
+endif()
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/CMakeLists.txt b/externals/coda-oss/modules/c++/coda_oss.json/CMakeLists.txt
new file mode 100644
index 000000000..70268ecf0
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(MODULE_NAME coda_oss.json)
+
+coda_add_module(${MODULE_NAME}
+ VERSION 1.0
+ DEPS math.linear-c++ math.poly-c++ types-c++ mem-c++ nlohmann-c++)
+
+coda_add_tests(
+ MODULE_NAME ${MODULE_NAME}
+ DIRECTORY "unittests"
+ UNITTEST
+ SOURCES
+ test_json_math.cpp
+ test_json_mem.cpp
+ test_json_std.cpp
+ test_json_types.cpp)
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Math.h b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Math.h
new file mode 100644
index 000000000..f3ee2244d
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Math.h
@@ -0,0 +1,155 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-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 .
+ */
+
+#pragma once
+#ifndef CODA_OSS_json_math_h_INCLUDED_
+#define CODA_OSS_json_math_h_INCLUDED_
+
+#include
+#include
+
+namespace math
+{
+ namespace linear
+ {
+ template
+ void to_json(BasicJsonType& j, const VectorN& v)
+ {
+ std::array arr;
+ for (size_t idx = 0; idx < N; idx++)
+ {
+ arr[idx] = v.matrix()[idx][0];
+ }
+ j = arr;
+ }
+ template
+ void from_json(const BasicJsonType& j, VectorN& v)
+ {
+ v = j.template get>();
+ }
+
+ template
+ void to_json(BasicJsonType& j, const MatrixMxN& v)
+ {
+ std::array, M> arr;
+ for (size_t iRow = 0; iRow < M; iRow++)
+ {
+ for (size_t iCol = 0; iCol < N; iCol++)
+ {
+ arr[iRow][iCol] = v.mRaw[iRow][iCol];
+ }
+ }
+ j = arr;
+ }
+ template
+ void from_json(const BasicJsonType& j, MatrixMxN& v)
+ {
+ auto temp = j.template get< std::array< std::array, M> > ();
+ T arr[M * N];
+ for (size_t iRow = 0; iRow < M; iRow++)
+ {
+ for (size_t iCol = 0; iCol < N; iCol++)
+ {
+ arr[iRow * N + iCol] = temp[iRow][iCol];
+ }
+ }
+ v = arr;
+ }
+
+ } // namespace linear
+
+ namespace poly
+ {
+ template
+ void to_json(BasicJsonType& j, const OneD& poly)
+ {
+ if (poly.empty())
+ {
+ return;
+ }
+ j = poly.coeffs();
+ }
+ template
+ void from_json(const BasicJsonType& j, OneD& poly)
+ {
+ if (j.is_null())
+ {
+ poly = OneD();
+ return;
+ }
+ poly = OneD(j.template get>());
+ }
+
+ template
+ void to_json(BasicJsonType& j, const TwoD& poly)
+ {
+ if (poly.empty())
+ {
+ return;
+ }
+ std::vector> coeffs(poly.orderX() + 1);
+ for (size_t ix = 0; ix <= poly.orderX(); ix++)
+ {
+ coeffs[ix] = poly[ix].coeffs();
+ }
+ j = coeffs;
+ }
+ template
+ void from_json(const BasicJsonType& j, TwoD& poly)
+ {
+ if (j.is_null())
+ {
+ poly = TwoD();
+ return;
+ }
+ poly = TwoD(j.template get>>());
+ }
+
+ template
+ void to_json(BasicJsonType& j, const Fixed1D& poly)
+ {
+ j = poly.coeffs();
+ }
+ template
+ void from_json(const BasicJsonType& j, Fixed1D& poly)
+ {
+ poly.coeffs() = j.template get>();
+ }
+
+ template
+ void to_json(BasicJsonType& j, const Fixed2D& poly)
+ {
+ std::array, OX + 1> coeffs;
+ for (size_t ix = 0; ix <= poly.orderX(); ix++)
+ {
+ coeffs[ix] = poly[ix].coeffs();
+ }
+ j = coeffs;
+ }
+ template
+ void from_json(const BasicJsonType& j, Fixed2D& poly)
+ {
+ poly = TwoD(j.template get>>());
+ }
+ } // namespace poly
+} // namespace math
+
+#endif
\ No newline at end of file
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Mem.h b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Mem.h
new file mode 100644
index 000000000..c8ac08c47
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Mem.h
@@ -0,0 +1,45 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-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 .
+ */
+
+#pragma once
+#ifndef CODA_OSS_json_mem_h_INCLUDED_
+#define CODA_OSS_json_mem_h_INCLUDED_
+
+#include
+
+namespace mem
+{
+ template
+ void to_json(BasicJsonType& j, const ScopedPtr& p)
+ {
+ // If 'p' is not `nullptr`, serialize a type T
+ if (p) j = *p;
+ }
+ template
+ void from_json(const BasicJsonType& j, ScopedPtr& p)
+ {
+ // If the json is `null` reset with a`nullptr`
+ // otherwise, deserialize a 'T'
+ p.reset(j.is_null() ? nullptr : new T(j.template get()));
+ }
+} // namespace mem
+
+#endif
\ No newline at end of file
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Std.h b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Std.h
new file mode 100644
index 000000000..1069e8450
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Std.h
@@ -0,0 +1,90 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-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 .
+ *
+ */
+
+#pragma once
+#ifndef CODA_OSS_json_std_h_INCLUDED_
+#define CODA_OSS_json_std_h_INCLUDED_
+
+#include
+#include
+#include
+#include
+
+// Json definitions for some extra 'std' members.
+// https://github.com/nlohmann/json?tab=readme-ov-file#how-do-i-convert-third-party-types
+namespace nlohmann {
+ template
+ struct adl_serializer> {
+ template
+ static void to_json(BasicJsonType& j, const std::complex& z) {
+ j["real"] = z.real();
+ j["imag"] = z.imag();
+ }
+ template
+ static void from_json(const BasicJsonType& j, std::complex& z) {
+ z.real(j["real"].template get());
+ z.imag(j["imag"].template get());
+ }
+ };
+
+ template
+ struct adl_serializer> {
+ template
+ static void to_json(BasicJsonType& j, const std::unique_ptr& p) {
+ if (p) j = *p;
+ }
+ template
+ static void from_json(const BasicJsonType& j, std::unique_ptr& p) {
+ p.reset(j.is_null() ? nullptr : new T(j.template get()));
+ }
+ };
+
+ template
+ struct adl_serializer> {
+ template
+ static void to_json(BasicJsonType& j, const std::shared_ptr& p) {
+ if (p) j = *p;
+ }
+ template
+ static void from_json(const BasicJsonType& j, std::shared_ptr& p) {
+ p.reset(j.is_null() ? nullptr : new T(j.template get()));
+ }
+ };
+
+ template
+ struct adl_serializer> {
+ template
+ static void to_json(BasicJsonType& j, const std::optional& p) {
+ if (p.has_value()) j = p.value();
+ }
+ template
+ static void from_json(const BasicJsonType& j, std::optional& p) {
+ if (j.is_null())
+ {
+ p.reset();
+ return;
+ }
+ p.emplace(j.template get());
+ }
+ };
+}
+#endif
\ No newline at end of file
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Types.h b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Types.h
new file mode 100644
index 000000000..365e334b0
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/include/coda_oss/json/Types.h
@@ -0,0 +1,103 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-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 .
+ *
+ */
+
+#pragma once
+#ifndef CODA_OSS_json_types_h_INCLUDED_
+#define CODA_OSS_json_types_h_INCLUDED_
+
+#include
+#include
+#include
+
+// Json definitions for the 'types' module.
+namespace types
+{
+ template
+ void to_json(BasicJsonType& j, const Complex& z)
+ {
+ j["real"] = z.real();
+ j["imag"] = z.imag();
+ }
+ template
+ void from_json(const BasicJsonType& j, Complex& z)
+ {
+ z.real(j["real"].template get());
+ z.imag(j["imag"].template get());
+ }
+
+ template
+ void to_json(BasicJsonType& j, const PageRowCol& prc)
+ {
+ j["page"] = prc.page;
+ j["row"] = prc.row;
+ j["col"] = prc.col;
+ }
+ template
+ void from_json(const BasicJsonType& j, PageRowCol& prc)
+ {
+ prc.page = j["page"].template get();
+ prc.row = j["row"].template get();
+ prc.col = j["col"].template get();
+ }
+
+ template
+ void to_json(BasicJsonType& j, const RangeList& list)
+ {
+ j = list.getRanges();
+ }
+ template
+ void from_json(const BasicJsonType& j, RangeList& list)
+ {
+ list.insert(j.template get>());
+ }
+
+ template
+ void to_json(BasicJsonType& j, const RgAz& ra)
+ {
+ j["rg"] = ra.rg;
+ j["az"] = ra.az;
+ }
+ template
+ void from_json(const BasicJsonType& j, RgAz& ra)
+ {
+ ra.rg = j["rg"].template get();
+ ra.az = j["az"].template get();
+ }
+
+ template
+ void to_json(BasicJsonType& j, const RowCol& rc)
+ {
+ j["row"] = rc.row;
+ j["col"] = rc.col;
+ }
+ template
+ void from_json(const BasicJsonType& j, RowCol& rc)
+ {
+ rc.row = j["row"].template get();
+ rc.col = j["col"].template get();
+ }
+
+ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Range, mStartElement, mNumElements)
+} // namespace types
+
+
+#endif
\ No newline at end of file
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/include/import/coda_oss/json.h b/externals/coda-oss/modules/c++/coda_oss.json/include/import/coda_oss/json.h
new file mode 100644
index 000000000..843510321
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/include/import/coda_oss/json.h
@@ -0,0 +1,31 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-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 __IMPORT_CODA_OSS_JSON_H__
+#define __IMPORT_CODA_OSS_JSON_H__
+
+#include
+#include
+#include
+#include
+#include
+
+#endif
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_math.cpp b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_math.cpp
new file mode 100644
index 000000000..95e1f91e2
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_math.cpp
@@ -0,0 +1,152 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-c++ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not,
+ * see .
+ */
+
+#include "TestCase.h"
+
+#include
+#include
+
+using json = nlohmann::json;
+
+TEST_CASE(TestVectorN)
+{
+ using VecI = math::linear::VectorN<2, int>;
+ using VecD = math::linear::VectorN<3, double>;
+
+ std::vector v0 = {
+ static_cast(0),
+ static_cast(1)};
+ VecI startVal0(v0);
+ json expected = v0;
+ json serialized = startVal0;
+ auto deserialized0 = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal0 == deserialized0);
+
+ std::vector v1 = {10., 20., 30.};
+ VecD startVal1(v1);
+ expected = v1;
+ serialized = startVal1;
+ auto deserialized1 = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal1 == deserialized1);
+}
+TEST_CASE(TestMatrixMxN)
+{
+ using TwoXTwo = math::linear::MatrixMxN<2, 2, int>;
+ using ThreeXTwo = math::linear::MatrixMxN<3, 2, int>;
+ using TwoXThree = math::linear::MatrixMxN<2, 3, double>;
+
+ TwoXTwo startVal0({0, 1, 2, 3});
+ json expected = std::vector>{{0, 1}, {2, 3}};
+ json serialized = startVal0;
+ auto deserialized0 = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal0 == deserialized0);
+
+ ThreeXTwo startVal1({0, 1, 2, 3, 4, 5});
+ expected = std::vector>{{0, 1}, {2, 3}, {4, 5}};
+ serialized = startVal1;
+ auto deserialized1 = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal1 == deserialized1);
+
+ TwoXThree startVal2({0., 1., 2., 3., 4., 5.});
+ expected = std::vector>{{0., 1., 2.}, {3., 4., 5.}};
+ serialized = startVal2;
+ auto deserialized2 = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal2 == deserialized2);
+}
+TEST_CASE(TestPolyOneD)
+{
+ using OneD = math::poly::OneD;
+ std::vector coeffs{0, 1, 2, 3};
+ OneD startVal(coeffs);
+ json serialized = startVal;
+ json expected = coeffs;
+ OneD deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestPolyTwoD)
+{
+ using OneD = math::poly::OneD;
+ using TwoD = math::poly::TwoD;
+ std::vector c0 = {0.1, 0.2, 0.3};
+ std::vector c1 = {-0.3, -0.2, -0.1};
+ OneD p0(c0);
+ OneD p1(c1);
+ TwoD startVal({p0, p1});
+ json serialized = startVal;
+ json expected = std::vector>{c0, c1};
+ auto deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestPolyXYZ)
+{
+ using XYZ = math::linear::VectorN<3>;
+ using OneDXYZ = math::poly::OneD;
+ std::vector c0 = {0.1, 0.2, 0.3};
+ std::vector c1 = {-0.3, -0.2, -0.1};
+ OneDXYZ startVal({XYZ(c0), XYZ(c1)});
+ json serialized = startVal;
+ json expected = {c0, c1};
+ auto deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestPolyFixed1D)
+{
+ using Fixed1D = math::poly::Fixed1D<3, int>;
+ std::vector coeffs{0, 1, 2, 3};
+ Fixed1D startVal(coeffs);
+ json serialized = startVal;
+ json expected = coeffs;
+ Fixed1D deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestPolyFixed2D)
+{
+ using Fixed1D = math::poly::Fixed1D<2, double>;
+ using Fixed2D = math::poly::Fixed2D<1, 2, double>;
+ std::vector c0 = {0.1, 0.2, 0.3};
+ std::vector c1 = {-0.3, -0.2, -0.1};
+ Fixed1D p0(c0);
+ Fixed1D p1(c1);
+ Fixed2D startVal({p0, p1});
+ json serialized = startVal;
+ json expected = std::vector>{c0, c1};
+ auto deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+
+
+TEST_MAIN(
+ TEST_CHECK(TestVectorN);
+ TEST_CHECK(TestMatrixMxN);
+ TEST_CHECK(TestPolyOneD);
+ TEST_CHECK(TestPolyTwoD);
+ TEST_CHECK(TestPolyXYZ);
+)
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_mem.cpp b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_mem.cpp
new file mode 100644
index 000000000..4d267aab2
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_mem.cpp
@@ -0,0 +1,69 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-c++ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not,
+ * see .
+ */
+
+#include "TestCase.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+using json = nlohmann::json;
+
+TEST_CASE(TestCloneablePtr)
+{
+ using RC = types::RowCol;
+ RC val(-1, 1);
+ mem::ScopedCloneablePtr startVal(new RC(val));
+ json expected = {{"row", val.row}, {"col", val.col}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+
+TEST_CASE(TestCopyablePtr)
+{
+ int val = 42;
+ mem::ScopedCopyablePtr startVal(new int(val));
+ json expected = val;
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestNullPtr)
+{
+ mem::ScopedCopyablePtr startVal;
+ json expected = nullptr;
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+
+TEST_MAIN(
+ TEST_CHECK(TestCloneablePtr);
+ TEST_CHECK(TestCopyablePtr);
+ TEST_CHECK(TestNullPtr);
+)
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_std.cpp b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_std.cpp
new file mode 100644
index 000000000..0627e9cd7
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_std.cpp
@@ -0,0 +1,104 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-c++ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not,
+ * see .
+ */
+
+#include "TestCase.h"
+
+#include
+
+#include
+#include
+
+using json = nlohmann::json;
+using namespace std;
+
+TEST_CASE(TestStdComplex)
+{
+ complex startVal(-1, 1);
+ json expected = {{"real", startVal.real()}, {"imag", startVal.imag()}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestStdSharedVal)
+{
+ shared_ptr> startVal(new complex(-1, 1));
+ json expected = {{"real", startVal->real()}, {"imag", startVal->imag()}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(*startVal == *deserialized);
+}
+TEST_CASE(TestStdSharedNull)
+{
+ shared_ptr> startVal;
+ json expected = nullptr;
+ json serialized = startVal;
+ auto deserialized = serialized.template get>>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestStdUniqueVal)
+{
+ unique_ptr> startVal(new complex(-1, 1));
+ json expected = {{"real", startVal->real()}, {"imag", startVal->imag()}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(*startVal == *deserialized);
+}
+TEST_CASE(TestStdUniqueNull)
+{
+ unique_ptr> startVal;
+ json expected = nullptr;
+ json serialized = startVal;
+ auto deserialized = serialized.template get>>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestStdOptionVal)
+{
+ optional startVal(0);
+ json expected = 0;
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestStdOptionEmpty)
+{
+ optional startVal;
+ json expected = nullptr;
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+
+TEST_MAIN(
+ TEST_CHECK(TestStdComplex);
+ TEST_CHECK(TestStdSharedVal);
+ TEST_CHECK(TestStdSharedNull);
+ TEST_CHECK(TestStdUniqueVal);
+ TEST_CHECK(TestStdUniqueNull);
+ TEST_CHECK(TestStdOptionVal);
+ TEST_CHECK(TestStdOptionEmpty);
+)
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_types.cpp b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_types.cpp
new file mode 100644
index 000000000..6ad3a86bc
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/unittests/test_json_types.cpp
@@ -0,0 +1,110 @@
+/* =========================================================================
+ * This file is part of coda-oss.json-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025 ARKA Group, L.P. All rights reserved
+ *
+ * types-c++ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not,
+ * see .
+ */
+
+#include "TestCase.h"
+
+#include
+
+using json = nlohmann::json;
+
+TEST_CASE(TestComplex)
+{
+ types::Complex startVal(1, 2);
+ json expected = {{"real", startVal.real()}, {"imag", startVal.imag()}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestRange)
+{
+ types::Range startVal(0, 10);
+ json expected = {
+ {"mStartElement", startVal.mStartElement},
+ {"mNumElements", startVal.mNumElements}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestRangeList)
+{
+ types::RangeList startVal;
+ types::Range r0(0, 10);
+ types::Range r1(15, 15);
+ startVal.insert(r0);
+ startVal.insert(r1);
+ json expected = {
+ {
+ {"mStartElement", r0.mStartElement},
+ {"mNumElements", r0.mNumElements}
+ },
+ {
+ {"mStartElement", r1.mStartElement},
+ {"mNumElements", r1.mNumElements}
+ },
+
+ };
+ json serialized = startVal;
+ auto deserialized = serialized.template get();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestRgAz)
+{
+ types::RgAz startVal(0.0, 1.0);
+ json expected = {{"rg", startVal.rg}, {"az", startVal.az}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestRowCol)
+{
+ types::RowCol startVal(0.0, 1.0);
+ json expected = {{"row", startVal.row}, {"col", startVal.col}};
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+TEST_CASE(TestPageRowCol)
+{
+ types::PageRowCol startVal(0, 1, 2);
+ json expected = {
+ {"page", startVal.page},
+ {"row", startVal.row},
+ {"col", startVal.col}
+ };
+ json serialized = startVal;
+ auto deserialized = serialized.template get>();
+ TEST_ASSERT(serialized == expected);
+ TEST_ASSERT(startVal == deserialized);
+}
+
+TEST_MAIN(
+ TEST_CHECK(TestComplex);
+ TEST_CHECK(TestRange);
+ TEST_CHECK(TestRangeList);
+ TEST_CHECK(TestRgAz);
+ TEST_CHECK(TestRowCol);
+ TEST_CHECK(TestPageRowCol);
+)
diff --git a/externals/coda-oss/modules/c++/coda_oss.json/wscript b/externals/coda-oss/modules/c++/coda_oss.json/wscript
new file mode 100644
index 000000000..490b54d83
--- /dev/null
+++ b/externals/coda-oss/modules/c++/coda_oss.json/wscript
@@ -0,0 +1,13 @@
+NAME = 'coda_oss.json'
+VERSION = '1.0'
+MODULE_DEPS = 'math.linear math.poly types mem nlohmann'
+
+options = distclean = lambda p: None
+from waflib import Options
+
+def configure(conf):
+ conf.env['HAVE_NLOHMANN'] = Options.options.enable_nlohmann
+
+def build(bld):
+ if bld.env['HAVE_NLOHMANN']:
+ bld.module(**globals())
\ No newline at end of file
diff --git a/externals/coda-oss/modules/c++/io/CMakeLists.txt b/externals/coda-oss/modules/c++/io/CMakeLists.txt
index 14ca6221b..58accb793 100644
--- a/externals/coda-oss/modules/c++/io/CMakeLists.txt
+++ b/externals/coda-oss/modules/c++/io/CMakeLists.txt
@@ -3,7 +3,7 @@ set(MODULE_NAME io)
coda_add_module(
${MODULE_NAME}
VERSION 1.0
- DEPS sys-c++ mem-c++ std-c++ gsl-c++
+ DEPS sys-c++ mem-c++ std-c++ gsl-c++ mt-c++
SOURCE_FILTER "MMapInputStream.cpp")
coda_add_tests(
diff --git a/externals/coda-oss/modules/c++/io/include/io/FileInputStreamOS.h b/externals/coda-oss/modules/c++/io/include/io/FileInputStreamOS.h
index 6c16641b7..371363d75 100644
--- a/externals/coda-oss/modules/c++/io/include/io/FileInputStreamOS.h
+++ b/externals/coda-oss/modules/c++/io/include/io/FileInputStreamOS.h
@@ -44,6 +44,9 @@
namespace io
{
+constexpr size_t defaultNumThreads = 1;
+constexpr size_t defaultChunkSize = 32L * 1024L * 1024L;
+constexpr size_t defaultMinChunksForThreading = 4;
/*!
* \class FileInputStreamOS
@@ -57,6 +60,10 @@ struct CODA_OSS_API FileInputStreamOS : public SeekableInputStream
{
protected:
sys::File mFile;
+ size_t mMaxReadThreads;
+ size_t mParallelChunkSize;
+ size_t mMinChunksForThreading;
+
public:
FileInputStreamOS() = default;
@@ -66,7 +73,10 @@ struct CODA_OSS_API FileInputStreamOS : public SeekableInputStream
* \param inputFile The file name
* \param mode The mode to open the file in
*/
- FileInputStreamOS(const std::string& inputFile)
+ FileInputStreamOS(const std::string& inputFile) :
+ mMaxReadThreads(defaultNumThreads),
+ mParallelChunkSize(defaultChunkSize),
+ mMinChunksForThreading(defaultMinChunksForThreading)
{
// Let this SystemException slide for now
mFile.create(inputFile,
@@ -78,7 +88,10 @@ struct CODA_OSS_API FileInputStreamOS : public SeekableInputStream
FileInputStreamOS(const char* inputFile) : // "file.txt" could be either std::string or std::filesystem::path
FileInputStreamOS(std::string(inputFile)) { }
- FileInputStreamOS(const sys::File& inputFile)
+ FileInputStreamOS(const sys::File& inputFile) :
+ mMaxReadThreads(defaultNumThreads),
+ mParallelChunkSize(defaultChunkSize),
+ mMinChunksForThreading(defaultMinChunksForThreading)
{
mFile = inputFile;
}
@@ -165,6 +178,67 @@ struct CODA_OSS_API FileInputStreamOS : public SeekableInputStream
mFile.close();
}
+ /*!
+ * Set the limit of the number of parallel read threads. Upon
+ * construction, value is set to 1 read thread. This sets a limit which is
+ * considered for every read() call on the object. Parallel reading is
+ * only enabled if the read is at least
+ * getParallelChunkSize()*setMinimumChunkCount() bytes, and if
+ * getMaxReadThreads() is larger than 1.
+ * \param maxReadThreads Maximum number of parallel read threads
+ */
+ void setMaxReadThreads(size_t maxReadThreads)
+ {
+ mMaxReadThreads = maxReadThreads;
+ }
+
+ /*!
+ * Get the limit of the number of parallel read threads.
+ * \return Maximum number of parallel read threads
+ */
+ size_t getMaxReadThreads() const
+ {
+ return mMaxReadThreads;
+ }
+
+ /*!
+ * Set the chunked read size when doing parallel reads.
+ * \param chunkSize Chunk size, in bytes
+ */
+ void setParallelChunkSize(size_t chunkSize)
+ {
+ mParallelChunkSize = chunkSize;
+ }
+
+ /*!
+ * Get the chunked read size for parallel reads
+ * \return Chunk size, in bytes
+ */
+ size_t getParallelChunkSize() const
+ {
+ return mParallelChunkSize;
+ }
+
+ /*!
+ * Set the minimum number of chunks (of size getParallelChunkSize() bytes)
+ * for enabling parallel reading
+ * \param minChunks Minimum chunk count
+ */
+ void setMinimumChunkCount(size_t minChunks)
+ {
+ mMinChunksForThreading = minChunks;
+ }
+
+ /*!
+ * Get the minimum number of chunks (of size getParallelChunkSize() bytes)
+ * for enabling parallel reading
+ * \return Minimum chunk count
+ */
+ size_t getMinimumChunkCount() const
+ {
+ return mMinChunksForThreading;
+ }
+
protected:
/*!
* Read up to len bytes of data from input stream into an array
diff --git a/externals/coda-oss/modules/c++/io/source/FileInputStreamOS.cpp b/externals/coda-oss/modules/c++/io/source/FileInputStreamOS.cpp
index 8ad36f147..064334d87 100644
--- a/externals/coda-oss/modules/c++/io/source/FileInputStreamOS.cpp
+++ b/externals/coda-oss/modules/c++/io/source/FileInputStreamOS.cpp
@@ -1,7 +1,7 @@
/* =========================================================================
- * This file is part of io-c++
+ * This file is part of io-c++
* =========================================================================
- *
+ *
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
*
* io-c++ is free software; you can redistribute it and/or modify
@@ -14,23 +14,55 @@
* 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,
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not,
* see .
*
*/
#include "io/FileInputStreamOS.h"
+#include "mt/ThreadGroup.h"
+#include "mt/ThreadPlanner.h"
+
#if !defined(USE_IO_STREAMS)
+namespace
+{
+class ChunkReadRunnable : public sys::Runnable
+{
+public:
+ ChunkReadRunnable(sys::File& file,
+ size_t offset,
+ size_t len,
+ void* buffer) :
+ mFile(file), mOffset(offset), mLen(len), mBuffer(buffer)
+ {
+ }
+
+ void run() override
+ {
+ // No need to clear buffer because the readInto call will write every
+ // byte
+ //::memset(mBuffer, 0, mLen);
+ mFile.readAtInto(mOffset, mBuffer, mLen);
+ }
+
+private:
+ sys::File& mFile;
+ size_t mOffset;
+ size_t mLen;
+ void* mBuffer;
+};
+}
+
/*!
* Returns the number of bytes that can be read
* without blocking by the next caller of a method for this input
- *
+ *
* \throw except::IOException
* \return number of bytes which are readable
- *
+ *
*/
sys::Off_T io::FileInputStreamOS::available()
{
@@ -45,14 +77,56 @@ sys::Off_T io::FileInputStreamOS::available()
sys::SSize_T io::FileInputStreamOS::readImpl(void* buffer, size_t len)
{
- ::memset(buffer, 0, len);
sys::Off_T avail = available();
+ sys::byte* bufferPtr = static_cast(buffer);
if (!avail)
+ {
+ // Clear the buffer to preserve existing (undocumented) behavior
+ ::memset(buffer, 0, len);
return io::InputStream::IS_EOF;
+ }
if (len > static_cast(avail))
+ {
+ // Clear just the tail end of the buffer to preserve existing
+ // (undocumented) behavior
+ ::memset(bufferPtr + avail, 0, len - avail);
len = static_cast(avail);
+ }
+
+ if (mMaxReadThreads <= 1 ||
+ len <= mParallelChunkSize * mMinChunksForThreading)
+ {
+ // No need to clear buffer because the readInto call will write every
+ // byte
+ //::memset(buffer, 0, len);
+ mFile.readInto(buffer, len);
+ return static_cast(len);
+ }
+
+ size_t baseLocation = tell();
+
+ size_t chunks = len / mParallelChunkSize;
+ const mt::ThreadPlanner planner(chunks, mMaxReadThreads);
+ mt::ThreadGroup threadGroup;
+
+ size_t threadNum(0);
+ size_t threadOffset;
+ size_t threadNumChunks;
+ while (planner.getThreadInfo(threadNum++, threadOffset, threadNumChunks))
+ {
+ size_t bufferOffset = threadOffset * mParallelChunkSize;
+ threadGroup.createThread(
+ new ChunkReadRunnable(mFile,
+ baseLocation + bufferOffset,
+ threadNumChunks * mParallelChunkSize,
+ bufferPtr + bufferOffset));
+ }
+
+ threadGroup.joinAll();
- mFile.readInto(buffer, len);
+ size_t threadedRead = chunks * mParallelChunkSize;
+ seek(baseLocation + threadedRead, START);
+ mFile.readInto(bufferPtr + threadedRead, len - threadedRead);
return static_cast(len);
}
diff --git a/externals/coda-oss/modules/c++/io/unittests/test_parallel_reads.cpp b/externals/coda-oss/modules/c++/io/unittests/test_parallel_reads.cpp
new file mode 100644
index 000000000..cdfd04fe6
--- /dev/null
+++ b/externals/coda-oss/modules/c++/io/unittests/test_parallel_reads.cpp
@@ -0,0 +1,85 @@
+/* =========================================================================
+ * This file is part of io-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2025, Arka Group, L.P.
+ *
+ * io-c++ is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not,
+ * see .
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+std::string thisExecutable;
+
+TEST_CASE(testParallelReads)
+{
+ // using self is easier than carting around extra files in the repo
+ std::cout << "thisExecutable: " << thisExecutable << std::endl;
+
+ // standard library read as a reference
+ std::ifstream from(thisExecutable, std::ios::binary);
+ from.seekg(0, std::ios::end);
+ size_t trueLen = from.tellg();
+ from.seekg(0);
+
+ std::vector referenceRead(trueLen);
+ from.read(&referenceRead[0], trueLen);
+ from.close();
+
+ // test FileInputStreamOS different ways
+ io::FileInputStreamOS fis(thisExecutable);
+
+ // ensure threading is disabled by default
+ TEST_ASSERT_EQ(fis.getMaxReadThreads(), 1);
+
+ // single thread read
+ size_t avail = fis.available();
+ TEST_ASSERT_EQ(avail, trueLen);
+
+ std::vector singleThreadRead(avail);
+ size_t wasRead = fis.read(&singleThreadRead[0], avail);
+ TEST_ASSERT_EQ(wasRead, avail);
+ TEST_ASSERT_TRUE(referenceRead == singleThreadRead);
+
+ fis.seek(0, io::Seekable::START);
+
+ // setup multithread read
+ fis.setMaxReadThreads(16);
+ fis.setMinimumChunkCount(4);
+ fis.setParallelChunkSize(1024);
+
+ // check set/get methods
+ TEST_ASSERT_EQ(fis.getMaxReadThreads(), 16);
+ TEST_ASSERT_EQ(fis.getMinimumChunkCount(), 4);
+ TEST_ASSERT_EQ(fis.getParallelChunkSize(), 1024);
+
+ std::vector multiThreadRead(avail);
+ wasRead = fis.read(&multiThreadRead[0], avail);
+ TEST_ASSERT_EQ(wasRead, avail);
+ TEST_ASSERT_TRUE(referenceRead == multiThreadRead);
+}
+
+int main(int, char* argv[])
+{
+ thisExecutable = std::string(argv[0]);
+ TEST_CHECK(testParallelReads);
+ return 0;
+}
diff --git a/externals/coda-oss/modules/c++/io/wscript b/externals/coda-oss/modules/c++/io/wscript
index 9448a5a6e..ab1268c31 100644
--- a/externals/coda-oss/modules/c++/io/wscript
+++ b/externals/coda-oss/modules/c++/io/wscript
@@ -1,6 +1,6 @@
NAME = 'io'
VERSION = '1.0'
-MODULE_DEPS = 'sys mem std gsl'
+MODULE_DEPS = 'sys mem std gsl mt'
SOURCE_FILTER = 'MMapInputStream.cpp'
TEST_FILTER = 'mmByteStreamTest.cpp'
diff --git a/externals/coda-oss/modules/c++/logging/include/logging/Handler.h b/externals/coda-oss/modules/c++/logging/include/logging/Handler.h
index a9df56434..e9313e81f 100644
--- a/externals/coda-oss/modules/c++/logging/include/logging/Handler.h
+++ b/externals/coda-oss/modules/c++/logging/include/logging/Handler.h
@@ -3,6 +3,7 @@
* =========================================================================
*
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
+ * (C) Copyright 2025-26 ARKA Group, L.P. All rights reserved
*
* logging-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
@@ -28,6 +29,8 @@
#define CODA_OSS_logging_Handler_h_INCLUDED_
#include
+#include
+
#include "config/Exports.h"
#include "logging/LogRecord.h"
#include "logging/Formatter.h"
@@ -94,7 +97,7 @@ struct CODA_OSS_API Handler : public Filterer
virtual void emitRecord(const LogRecord* record) = 0;
LogLevel mLevel = LogLevel::LOG_NOTSET;
- sys::Mutex mHandlerLock;
+ std::recursive_mutex mHandlerLock;
Formatter* mFormatter = nullptr;
StandardFormatter mDefaultFormatter;
};
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 2b33c9065..a9e5c5dc9 100644
--- a/externals/coda-oss/modules/c++/logging/include/logging/Logger.h
+++ b/externals/coda-oss/modules/c++/logging/include/logging/Logger.h
@@ -3,6 +3,7 @@
* =========================================================================
*
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
+ * (C) Copyright 2025-26 ARKA Group, L.P. All rights reserved
*
* logging-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
@@ -142,6 +143,14 @@ struct CODA_OSS_API Logger : public Filterer
return mName;
}
+ /*!
+ * Returns the current log level of the first handler, if any. Otherwise
+ * returns LogLevel::LOG_NOTSET. Note that each handler may have a
+ * different log level. However, the setLevel function changes the level
+ * of all handlers attached to this logger.
+ */
+ LogLevel getLevel();
+
//! Removes all handlers
void reset();
diff --git a/externals/coda-oss/modules/c++/logging/source/Handler.cpp b/externals/coda-oss/modules/c++/logging/source/Handler.cpp
index 52bd289c7..4362ac6ba 100644
--- a/externals/coda-oss/modules/c++/logging/source/Handler.cpp
+++ b/externals/coda-oss/modules/c++/logging/source/Handler.cpp
@@ -3,6 +3,7 @@
* =========================================================================
*
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
+ * (C) Copyright 2025-26 ARKA Group, L.P. All rights reserved
*
* logging-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
@@ -57,7 +58,7 @@ bool Handler::handle(const LogRecord* record)
if (filter(record))
{
//acquire lock
- mt::CriticalSection lock(&mHandlerLock);
+ mt::CriticalSection lock(&mHandlerLock);
try
{
emitRecord(record);
diff --git a/externals/coda-oss/modules/c++/logging/source/Logger.cpp b/externals/coda-oss/modules/c++/logging/source/Logger.cpp
index 3f3837537..3febfa68b 100644
--- a/externals/coda-oss/modules/c++/logging/source/Logger.cpp
+++ b/externals/coda-oss/modules/c++/logging/source/Logger.cpp
@@ -3,6 +3,7 @@
* =========================================================================
*
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
+ * (C) Copyright 2025-26 ARKA Group, L.P. All rights reserved
*
* logging-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
@@ -239,3 +240,16 @@ void logging::Logger::reset()
}
mHandlers.clear();
}
+
+logging::LogLevel logging::Logger::getLevel()
+{
+ LogLevel level = LogLevel::LOG_NOTSET;
+ if (!mHandlers.empty())
+ {
+ Handler_T handler = mHandlers.front();
+ level = handler.first->getLevel();
+ }
+
+ return level;
+}
+
diff --git a/externals/coda-oss/modules/c++/logging/source/StreamHandler.cpp b/externals/coda-oss/modules/c++/logging/source/StreamHandler.cpp
index 555522a80..2a208538f 100644
--- a/externals/coda-oss/modules/c++/logging/source/StreamHandler.cpp
+++ b/externals/coda-oss/modules/c++/logging/source/StreamHandler.cpp
@@ -3,6 +3,7 @@
* =========================================================================
*
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
+ * (C) Copyright 2025-26 ARKA Group, L.P. All rights reserved
*
* logging-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
@@ -111,7 +112,7 @@ void StreamHandler::write(const std::string& str)
if (!str.empty())
{
//acquire lock
- mt::CriticalSection lock(&mHandlerLock);
+ mt::CriticalSection lock(&mHandlerLock);
// write to stream
mStream->write(str);
diff --git a/externals/coda-oss/modules/c++/math.poly/include/math/poly/Fixed1D.h b/externals/coda-oss/modules/c++/math.poly/include/math/poly/Fixed1D.h
index 03024de73..4aa010b62 100644
--- a/externals/coda-oss/modules/c++/math.poly/include/math/poly/Fixed1D.h
+++ b/externals/coda-oss/modules/c++/math.poly/include/math/poly/Fixed1D.h
@@ -23,6 +23,8 @@
#ifndef __MATH_POLY_FIXED_1D_H__
#define __MATH_POLY_FIXED_1D_H__
+#include
+
#include
#include
#include