diff --git a/packages/HighFive/.github/run_examples.sh b/packages/HighFive/.github/run_examples.sh index 9af9a6ef1ba930f00b6c4b3d75265a67a06cfe4d..a60bd6d3ef66141076a167007b21f498df6cbdad 100755 --- a/packages/HighFive/.github/run_examples.sh +++ b/packages/HighFive/.github/run_examples.sh @@ -16,5 +16,10 @@ fi for f in "${examples_dir}"/*_bin do echo "-- ${f}" - "${f}" + if [[ "${f}" == *"parallel_"* ]] + then + mpiexec -np 2 "${f}" + else + "${f}" + fi done diff --git a/packages/HighFive/.github/workflows/ci.yml b/packages/HighFive/.github/workflows/ci.yml index 66069123da2df778caa0b3ad1c0f565a72ef7f36..f7379234f679097a6c890e1289b9c09251473f69 100644 --- a/packages/HighFive/.github/workflows/ci.yml +++ b/packages/HighFive/.github/workflows/ci.yml @@ -58,6 +58,9 @@ jobs: - config: os: ubuntu-22.04 flags: '-DHIGHFIVE_TEST_BOOST=Off -DCMAKE_CXX_STANDARD=20 -DHIGHFIVE_HAS_CONCEPTS=On' + - config: + os: ubuntu-24.04 + flags: '-DHIGHFIVE_TEST_BOOST=Off -DCMAKE_CXX_STANDARD=20 -DHIGHFIVE_HAS_CONCEPTS=On' steps: - uses: actions/checkout@v3 @@ -89,6 +92,9 @@ jobs: run: | ! ctest --verbose -C $BUILD_TYPE | grep HDF5-DIAG + - name: Examples + working-directory: ${{github.workspace}}/build/src/examples + run: $GITHUB_WORKSPACE/.github/run_examples.sh # Job testing several versions of hdf5 # =================================================== @@ -96,7 +102,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - hdf5_version : [ hdf5-1_8_23, hdf5-1_10_11, hdf5-1_12_3, hdf5_1.14.4.1 ] + hdf5_version : [ hdf5-1_8_23, hdf5-1_10_11, hdf5-1_12_3, hdf5_1.14.5 ] steps: - uses: actions/checkout@v3 @@ -133,7 +139,6 @@ jobs: run: | ! ctest --verbose -C $BUILD_TYPE | grep HDF5-DIAG - - name: Examples working-directory: ${{github.workspace}}/build/src/examples run: $GITHUB_WORKSPACE/.github/run_examples.sh @@ -184,13 +189,13 @@ jobs: # Job running unit-test with sanitizers # ===================================== Linux_Sanitizers: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: matrix: env: [ - {CC: clang-12, CXX: clang++-12, HIGHFIVE_SANITIZER: address}, - {CC: clang-12, CXX: clang++-12, HIGHFIVE_SANITIZER: undefined}, - {CC: gcc-10, CXX: g++-10, HIGHFIVE_GLIBCXX_ASSERTIONS: On}, + {CC: clang, CXX: clang++, HIGHFIVE_SANITIZER: address}, + {CC: clang, CXX: clang++, HIGHFIVE_SANITIZER: undefined}, + {CC: gcc, CXX: g++, HIGHFIVE_GLIBCXX_ASSERTIONS: On}, ] steps: @@ -201,7 +206,7 @@ jobs: - name: "Install libraries" run: | sudo apt-get -qq update - sudo apt-get -qq install libboost-all-dev libeigen3-dev libhdf5-dev libsz2 ninja-build + sudo apt-get -qq install boost1.83 libeigen3-dev libhdf5-dev libsz2 ninja-build - name: Build env: ${{matrix.env}} @@ -209,6 +214,7 @@ jobs: CMAKE_OPTIONS=( -GNinja -DHIGHFIVE_TEST_BOOST:BOOL=ON + -DHIGHFIVE_TEST_BOOST_SPAN:BOOL=ON -DHIGHFIVE_TEST_EIGEN:BOOL=ON -DHIGHFIVE_BUILD_DOCS:BOOL=FALSE -DHIGHFIVE_GLIBCXX_ASSERTIONS=${HIGHFIVE_GLIBCXX_ASSERTIONS:-OFF} @@ -297,9 +303,6 @@ jobs: cxxstd: ["14", "17", "20"] include: - - os: "macOS-12" - cxxstd: "14" - - os: "macOS-13" cxxstd: "20" diff --git a/packages/HighFive/.github/workflows/clang_format.yml b/packages/HighFive/.github/workflows/clang_format.yml index 7b9be4909f266a82c4d7ac79a38380e9a5f511bc..c8257b44c101acff9f9bd80f48de00b74c17b7ca 100644 --- a/packages/HighFive/.github/workflows/clang_format.yml +++ b/packages/HighFive/.github/workflows/clang_format.yml @@ -20,18 +20,4 @@ jobs: - name: Run clang-format run: | - clang-format --version - for i in $(git ls-files | grep ".[ch]pp$"); - do - clang-format -i "$i" > /dev/null 2>&1; - done - modified_files=$(git diff --name-only) - if [ -n "$modified_files" ]; - then - echo "Some files are not well formatted:" - echo $modified_files - echo "" - echo "The diff is:" - git diff - exit 1 - fi + bash bin/format.sh diff --git a/packages/HighFive/.gitrepo b/packages/HighFive/.gitrepo index 0512eb59369fa0287eb768cdb9215c29160d65a0..f6f59218375e4f81d3d7b90963cb95488066ebf0 100644 --- a/packages/HighFive/.gitrepo +++ b/packages/HighFive/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:BlueBrain/HighFive.git branch = master - commit = 20fbd4c4d328bc18a342eeef5ffd29f17de084e0 - parent = 35908346b20b524d4d2d9f8f6220800c27595413 + commit = dfc06537fdb20f5e572c20bc6febd22dd5d08246 + parent = fa321c14d4206f8183fc9029ee9bd0d216ce700b method = merge - cmdver = 0.4.6 + cmdver = 0.4.9 diff --git a/packages/HighFive/CHANGELOG.md b/packages/HighFive/CHANGELOG.md index fcd0247e37b370f52587440147d5bc4aa2cf541d..088c590bd0d9d435039432e55cdefaf499e093b1 100644 --- a/packages/HighFive/CHANGELOG.md +++ b/packages/HighFive/CHANGELOG.md @@ -1,4 +1,37 @@ # Changes +## Version 3.0.0-beta2 - 2024-12-04 +### New Features + - Support `boost::span`. (#1025) + +### Bug Fix + - Fix for not-quite null-terminated, fixed-length strings. (#1056) + - Guard target creation in `*Config.cmake`. (#1053) + +## Version 3.0.0-beta1 - 2024-07-16 +This version is a major one and is breaking some usage compare to v2. +Read the migration guide from the documentation: https://bluebrain.github.io/HighFive/md__2home_2runner_2work_2_high_five_2_high_five_2doc_2migration__guide.html + +The minimum version for C++ has been moved to `C++14`. + +### Removed + - Removed `read(T*, ...)`, use explicit `read_raw(T*, ...)` for `Slice` or `Attribute`. (#928) + - Removed `FixedLenStringArray`, use any container with strings instead. (#932) + - Removed `FileDriver` and `MPIOFileDriver`, use file access properties instead. (#949) + - Removed default constructor for `Group` and `DataSet`. (#947, #948) + - Broadcasting have been removed. Use `squeeze` and `reshape` feature instead. (#992) + - `ObjectCreateProps` and `ObjectAccessProps` those don't map well to HighFive and are unused. (#1002) + +### New Features + - Added support for `std::span`. (#987) + - Added `squeezeMemSpace` and `reshapeMemSpace` for `Attribute` and `Slice` to reshape the memory space. (#991) + - Added `ProductSet` to select a Cartesian products of (generalized) slices. (#842) + +### Improvements + - Optimized chained hyperslab selection. (#1031) + - Type `T[N]` or `T[N][M]` will work better. (#929) + - `DataspaceType` is now an enum class for `dataspace_scalar` or `dataspace_null`. (#900) + - `File::AccessMode` is now an enum class. (#1020) + ## Version 2.9.0 - 2024-01-25 ### New Features - Add named ctors for scalar and null dataspaces. (#899) diff --git a/packages/HighFive/CMakeLists.txt b/packages/HighFive/CMakeLists.txt index 2358e417203756c2e13358d4ba5322f89009f1a5..4aea70998781fd7d07987c92179c7c15d654a0d6 100644 --- a/packages/HighFive/CMakeLists.txt +++ b/packages/HighFive/CMakeLists.txt @@ -2,6 +2,12 @@ cmake_minimum_required(VERSION 3.13) cmake_policy(VERSION 3.13) project(HighFive VERSION 3.0.0) +set(HIGHFIVE_VERSION_PRERELEASE 2) + +message(WARNING "=================================================================\n" + "HighFive development moved to:\n" + " github.com/highfive-devs/highfive\n" + "=================================================================") # Configure HighFive # ------------------ @@ -58,12 +64,13 @@ option(HIGHFIVE_UNIT_TESTS "Compile unit-tests" ${HIGHFIVE_EXTRAS_DEFAULT}) option(HIGHFIVE_EXAMPLES "Compile examples" ${HIGHFIVE_EXTRAS_DEFAULT}) option(HIGHFIVE_BUILD_DOCS "Build documentation" ${HIGHFIVE_EXTRAS_DEFAULT}) -option(HIGHFIVE_TEST_SPAN "Enable std::span testing, requires C++20" ${HIGHFIVE_TEST_SPAN_DEFAULT}) -option(HIGHFIVE_TEST_BOOST "Enable Boost testing" OFF) -option(HIGHFIVE_TEST_EIGEN "Enable Eigen testing" OFF) -option(HIGHFIVE_TEST_OPENCV "Enable OpenCV testing" OFF) -option(HIGHFIVE_TEST_XTENSOR "Enable xtensor testing" OFF) -option(HIGHFIVE_TEST_HALF_FLOAT "Enable half-precision floats" OFF) +option(HIGHFIVE_TEST_SPAN "Enable testing std::span, requires C++20" ${HIGHFIVE_TEST_SPAN_DEFAULT}) +option(HIGHFIVE_TEST_BOOST "Enable testing Boost features" OFF) +option(HIGHFIVE_TEST_BOOST_SPAN "Additionally, enable testing `boost::span`" OFF) +option(HIGHFIVE_TEST_EIGEN "Enable testing Eigen" OFF) +option(HIGHFIVE_TEST_OPENCV "Enable testing OpenCV" OFF) +option(HIGHFIVE_TEST_XTENSOR "Enable testing xtensor" OFF) +option(HIGHFIVE_TEST_HALF_FLOAT "Enable testing half-precision floats" OFF) # TODO remove entirely. option(HIGHFIVE_HAS_CONCEPTS "Print readable compiler errors w/ C++20 concepts" OFF) @@ -155,6 +162,7 @@ install(FILES if(HIGHFIVE_EXAMPLES OR HIGHFIVE_UNIT_TESTS) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/HighFiveWarnings.cmake) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/HighFiveFlags.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/HighFiveOptionalDependencies.cmake) endif() diff --git a/packages/HighFive/README.md b/packages/HighFive/README.md index b8d71b357db079de7316c3565efd1bdd41e1bd82..dee6c73db056d7dcb45d835b843a3362a46ab618 100644 --- a/packages/HighFive/README.md +++ b/packages/HighFive/README.md @@ -1,15 +1,11 @@ -*Note:* In preparation of `v3` of HighFive, we've started merging breaking -changes into the main branch. More information and opportunity to comment can -be found at: -https://github.com/BlueBrain/HighFive/issues/864 +> [!WARNING] +> The Blue Brain Project concluded in December 2024, so the HighFive development is ceased under the BlueBrain GitHub organization. +> +> The development of HighFive will continue at: +> https://github.com/highfive-devs/highfive # HighFive - HDF5 header-only C++ Library -[](https://BlueBrain.github.io/HighFive/actions/workflows/gh-pages.yml?query=branch%3Amaster) -[](https://codecov.io/gh/BlueBrain/HighFive) -[](https://github.com/BlueBrain/HighFive-testing/actions/workflows/integration.yml) -[](https://zenodo.org/doi/10.5281/zenodo.10679422) - Documentation: https://bluebrain.github.io/HighFive/ ## Brief @@ -23,15 +19,16 @@ It integrates nicely with other CMake projects by defining (and exporting) a Hig ### Design - Simple C++-ish minimalist interface -- No other dependency than libhdf5 -- Zero overhead -- Support C++14 +- Only hard dependency is libhdf5 +- Zero/low overhead, when possible +- RAII for opening/closing files, groups, datasets, etc. +- Written in C++14 ### Feature support - create/read/write files, datasets, attributes, groups, dataspaces. - automatic memory management / ref counting - automatic conversion of `std::vector` and nested `std::vector` from/to any dataset with basic types -- automatic conversion of `std::string` to/from variable length string dataset +- automatic conversion of `std::string` to/from variable- or fixed-length string dataset - selection() / slice support - parallel Read/Write operations from several nodes with Parallel HDF5 - Advanced types: Compound, Enum, Arrays of Fixed-length strings, References @@ -41,221 +38,54 @@ It integrates nicely with other CMake projects by defining (and exporting) a Hig ### Dependencies - HDF5 or pHDF5, including headers -- boost >= 1.41 (recommended) +- boost (optional) - eigen3 (optional) - xtensor (optional) - half (optional) -### Known flaws -- HighFive is not thread-safe. At best it has the same limitations as the HDF5 library. However, HighFive objects modify their members without protecting these writes. Users have reported that HighFive is not thread-safe even when using the threadsafe HDF5 library, e.g., https://github.com/BlueBrain/HighFive/discussions/675. -- Eigen support in core HighFive was broken until v3.0. See https://github.com/BlueBrain/HighFive/issues/532. H5Easy was not - affected. -- The support of fixed length strings isn't ideal. +The releases for versions 2.x.y and two prereleases of v3 can be found at: +* https://github.com/BlueBrain/HighFive/releases +* https://zenodo.org/doi/10.5281/zenodo.10679422 +The state of HighFive immediately before preparing it for archival is: +* https://github.com/BlueBrain/HighFive/tree/v3.0.0-beta2 -## Examples +All future development and new releases can be found at: +* https://github.com/highfive-devs/highfive -#### Write a std::vector<int> to 1D HDF5 dataset and read it back +## Example ```c++ -#include <highfive/highfive.hpp> - using namespace HighFive; -std::string filename = "/tmp/new_file.h5"; +File file("foo.h5", File::Truncate); { - // We create an empty HDF55 file, by truncating an existing - // file if required: - File file(filename, File::Truncate); - std::vector<int> data(50, 1); file.createDataSet("grp/data", data); } { - // We open the file as read-only: - File file(filename, File::ReadOnly); auto dataset = file.getDataSet("grp/data"); - // Read back, with allocating: + // Read back, automatically allocating: auto data = dataset.read<std::vector<int>>(); - // Because `data` has the correct size, this will - // not cause `data` to be reallocated: + // Alternatively, if `data` has the correct + // size, without reallocation: dataset.read(data); } ``` -**Note:** As of 2.8.0, one can use `highfive/highfive.hpp` to include -everything HighFive. Prior to 2.8.0 one would include `highfive/H5File.hpp`. - -**Note:** For advanced usecases the dataset can be created without immediately -writing to it. This is common in MPI-IO related patterns, or when growing a -dataset over the course of a simulation. - -#### Write a 2 dimensional C double float array to a 2D HDF5 dataset - -See [create_dataset_double.cpp](https://github.com/BlueBrain/HighFive/blob/master/src/examples/create_dataset_double.cpp) - -#### Write and read a matrix of double float (boost::ublas) to a 2D HDF5 dataset - -See [boost_ublas_double.cpp](https://github.com/BlueBrain/HighFive/blob/master/src/examples/boost_ublas_double.cpp) - -#### Write and read a subset of a 2D double dataset - -See [select_partial_dataset_cpp11.cpp](https://github.com/BlueBrain/HighFive/blob/master/src/examples/select_partial_dataset_cpp11.cpp) - -#### Create, write and list HDF5 attributes - -See [create_attribute_string_integer.cpp](https://github.com/BlueBrain/HighFive/blob/master/src/examples/create_attribute_string_integer.cpp) - -#### And others - -See [src/examples/](https://github.com/BlueBrain/HighFive/blob/master/src/examples/) subdirectory for more info. - - -### H5Easy - -For several 'standard' use cases the [highfive/H5Easy.hpp](include/highfive/H5Easy.hpp) interface is available. It allows: - -* Reading/writing in a single line of: - - - scalars (to/from an extendible DataSet), - - strings, - - vectors (of standard types), - - [Eigen::Matrix](http://eigen.tuxfamily.org) (optional), - - [xt::xarray](https://github.com/QuantStack/xtensor) and [xt::xtensor](https://github.com/QuantStack/xtensor) - (optional). - - [cv::Mat_](https://docs.opencv.org/master/df/dfc/classcv_1_1Mat__.html) - (optional). - -* Getting in a single line: - - - the size of a DataSet, - - the shape of a DataSet. - -#### Example - -```cpp -#include <highfive/H5Easy.hpp> - -int main() { - H5Easy::File file("example.h5", H5Easy::File::Overwrite); - - int A = ...; - H5Easy::dump(file, "/path/to/A", A); - - A = H5Easy::load<int>(file, "/path/to/A"); -} -``` - -whereby the `int` type of this example can be replaced by any of the above -types. See [easy_load_dump.cpp](src/examples/easy_load_dump.cpp) for more -details. - -**Note:** Classes such as `H5Easy::File` are just short for the regular -`HighFive` classes (in this case `HighFive::File`). They can thus be used -interchangeably. - - -## CMake integration -There's two common paths of integrating HighFive into a CMake based project. -The first is to "vendor" HighFive, the second is to install HighFive as a -normal C++ library. Since HighFive makes choices about how to integrate HDF5, -sometimes following the third Bailout Approach is needed. - -Regular HDF5 CMake variables can be used. Interesting variables include: - -* `HDF5_USE_STATIC_LIBRARIES` to link statically against the HDF5 library. -* `HDF5_PREFER_PARALLEL` to prefer pHDF5. -* `HDF5_IS_PARALLEL` to check if HDF5 is parallel. - -Please consult `tests/cmake_integration` for examples of how to write libraries -or applications using HighFive. - -### Vendoring HighFive - -In this approach the HighFive sources are included in a subdirectory of the -project (typically as a git submodule), for example in `third_party/HighFive`. - -The projects `CMakeLists.txt` add the following lines -```cmake -add_subdirectory(third_party/HighFive) -target_link_libraries(foo HighFive) -``` - -**Note:** `add_subdirectory(third_party/HighFive)` will search and "link" HDF5 -but wont search or link any optional dependencies such as Boost. - -### Regular Installation of HighFive - -Alternatively, HighFive can be install and "found" like regular software. - -The project's `CMakeLists.txt` should add the following: -```cmake -find_package(HighFive REQUIRED) -target_link_libraries(foo HighFive) -``` - -**Note:** `find_package(HighFive)` will search for HDF5. "Linking" to -`HighFive` includes linking with HDF5. The two commands will not search for or -"link" to optional dependencies such as Boost. - -### Bailout Approach - -To prevent HighFive from searching or "linking" to HDF5 the project's -`CMakeLists.txt` should contain the following: - -```cmake -# Prevent HighFive CMake code from searching for HDF5: -set(HIGHFIVE_FIND_HDF5 Off) - -# Then "find" HighFive as usual: -find_package(HighFive REQUIRED) -# alternatively, when vendoring: -# add_subdirectory(third_party/HighFive) - -# Finally, use the target `HighFive::Include` which -# doesn't add a dependency on HDF5. -target_link_libraries(foo HighFive::Include) - -# Proceed to find and link HDF5 as required. -``` - -### Optional Dependencies - -HighFive does not attempt to find or "link" to any optional dependencies, such -as Boost, Eigen, etc. Any project using HighFive with any of the optional -dependencies must include the respective header: -``` -#include <highfive/boost.hpp> -#include <highfive/eigen.hpp> -``` -and add the required CMake code to find and link against the dependencies. For -Boost the required lines might be -``` -find_package(Boost REQUIRED) -target_link_libraries(foo PUBLIC Boost::headers) -``` - -# Questions? - -Do you have questions on how to use HighFive? Would you like to share an interesting example or -discuss HighFive features? Head over to the [Discussions](https://github.com/BlueBrain/HighFive/discussions) -forum and join the community. - -For bugs and issues please use [Issues](https://github.com/BlueBrain/HighFive/issues). - # Funding & Acknowledgment - + The development of this software was supported by funding to the Blue Brain Project, a research center of the École polytechnique fédérale de Lausanne (EPFL), from the Swiss government's ETH Board of the Swiss Federal Institutes of Technology. HighFive releases are uploaded to Zenodo. If you wish to cite HighFive in a scientific publication you can use the DOIs for the [Zenodo records](https://zenodo.org/doi/10.5281/zenodo.10679422). - -Copyright © 2015-2022 Blue Brain Project/EPFL + +Copyright © 2015-2024 Blue Brain Project/EPFL ### License diff --git a/packages/HighFive/bin/format.sh b/packages/HighFive/bin/format.sh new file mode 100755 index 0000000000000000000000000000000000000000..e2b2670aa6e5bb8054408c6a020ae6194cfda53f --- /dev/null +++ b/packages/HighFive/bin/format.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +clang_format_version="19.1.3" + +script_dir="$(dirname "${BASH_SOURCE[0]}")" +venv_dir="$script_dir/../.clang-format-venv" + +if [ ! -d "$venv_dir" ]; then + python3 -m venv "$venv_dir" + source "$venv_dir/bin/activate" + pip install clang-format=="$clang_format_version" + deactivate +fi + +source "$venv_dir/bin/activate" + +# Check if the installed version matches the expected version +installed_version="$(pip show clang-format | grep Version | cut -d ' ' -f 2)" + +if [ "$installed_version" != "$clang_format_version" ]; then + echo "Error: clang-format version mismatch. Expected $clang_format_version, got $installed_version" + echo "Please remove the virtual environment and run the script again:" + echo " rm -r \"$venv_dir\" && \"$0\"" + exit 1 +fi + +clang-format --version +for i in $(git ls-files | grep ".[ch]pp$"); do + clang-format -i "$i" > /dev/null 2>&1 +done + +modified_files=$(git diff --name-only) +if [ -n "$modified_files" ]; then + echo "Some files are not well formatted:" + echo "$modified_files" + echo "" + echo "The diff is:" + git --no-pager diff + echo "" + echo "To correct the formatting run:" + echo " $0" + exit 1 +fi diff --git a/packages/HighFive/cmake/HighFiveConfig.cmake b/packages/HighFive/cmake/HighFiveConfig.cmake index 33ce7b7b0ed225805954e8efedf3e2de0841778f..a263264cf22415157a7dcb842e1a3047f59ae922 100644 --- a/packages/HighFive/cmake/HighFiveConfig.cmake +++ b/packages/HighFive/cmake/HighFiveConfig.cmake @@ -8,12 +8,16 @@ if(HIGHFIVE_FIND_HDF5) find_dependency(HDF5) endif() -include("${CMAKE_CURRENT_LIST_DIR}/HighFiveTargets.cmake") +if(NOT TARGET HighFive) + include("${CMAKE_CURRENT_LIST_DIR}/HighFiveTargets.cmake") -if(HDF5_IS_PARALLEL) - find_dependency(MPI) - target_link_libraries(HighFive::HighFive INTERFACE MPI::MPI_C MPI::MPI_CXX) + if(HDF5_IS_PARALLEL) + find_dependency(MPI) + target_link_libraries(HighFive::HighFive INTERFACE MPI::MPI_C MPI::MPI_CXX) + endif() + + add_library(HighFive ALIAS HighFive::HighFive) + add_library(HighFiveInclude ALIAS HighFive::Include) endif() -add_library(HighFive ALIAS HighFive::HighFive) -add_library(HighFiveInclude ALIAS HighFive::Include) + diff --git a/packages/HighFive/cmake/HighFiveFlags.cmake b/packages/HighFive/cmake/HighFiveFlags.cmake new file mode 100644 index 0000000000000000000000000000000000000000..33be65e9c7efc58c52002f1f7b22a7d04ae2f0ad --- /dev/null +++ b/packages/HighFive/cmake/HighFiveFlags.cmake @@ -0,0 +1,30 @@ +if(TARGET HighFiveFlags) + # Allow multiple `include(HighFiveWarnings)`, which would + # attempt to redefine `HighFiveWarnings` and fail without + # this check. + return() +endif() + +add_library(HighFiveFlags INTERFACE) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(HIGHFIVE_MAX_ERRORS) + target_compile_options(HighFiveFlags + INTERFACE + -fmax-errors=${HIGHFIVE_MAX_ERRORS} + ) + endif() +endif() + +if(HIGHFIVE_GLIBCXX_ASSERTIONS) + target_compile_definitions(HighFiveFlags INTERFACE -D_GLIBCXX_ASSERTIONS) +endif() + +if(HIGHFIVE_HAS_FRIEND_DECLARATIONS) + target_compile_definitions(HighFiveFlags INTERFACE -DHIGHFIVE_HAS_FRIEND_DECLARATIONS=1) +endif() + +if(HIGHFIVE_SANITIZER) + target_compile_options(HighFiveFlags INTERFACE -fsanitize=${HIGHFIVE_SANITIZER}) + target_link_options(HighFiveFlags INTERFACE -fsanitize=${HIGHFIVE_SANITIZER}) +endif() diff --git a/packages/HighFive/cmake/HighFiveOptionalDependencies.cmake b/packages/HighFive/cmake/HighFiveOptionalDependencies.cmake index 861b80641fa3deb9d6849ca19c9b7369ee45fa3c..6c540b1b80a54adf58650cdfa366118badb4c4e3 100644 --- a/packages/HighFive/cmake/HighFiveOptionalDependencies.cmake +++ b/packages/HighFive/cmake/HighFiveOptionalDependencies.cmake @@ -7,6 +7,9 @@ if(NOT TARGET HighFiveBoostDependency) # -DBOOST_ALL_NO_LIB (does something on MSVC). target_compile_definitions(HighFiveBoostDependency INTERFACE HIGHFIVE_TEST_BOOST=1) endif() + if(HIGHFIVE_TEST_BOOST_SPAN) + target_compile_definitions(HighFiveBoostDependency INTERFACE HIGHFIVE_TEST_BOOST_SPAN=1) + endif() endif() if(NOT TARGET HighFiveEigenDependency) diff --git a/packages/HighFive/cmake/HighFiveWarnings.cmake b/packages/HighFive/cmake/HighFiveWarnings.cmake index a1dee19dcd42e37067497a87993ae1c701d4921c..2357a97caaedb416a9563bf97455ad31f51124a0 100644 --- a/packages/HighFive/cmake/HighFiveWarnings.cmake +++ b/packages/HighFive/cmake/HighFiveWarnings.cmake @@ -6,7 +6,6 @@ if(TARGET HighFiveWarnings) endif() add_library(HighFiveWarnings INTERFACE) -add_library(HighFiveFlags INTERFACE) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" @@ -48,11 +47,3 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") endif() endif() -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - if(HIGHFIVE_MAX_ERRORS) - target_compile_options(HighFiveFlags - INTERFACE - -fmax-errors=${HIGHFIVE_MAX_ERRORS} - ) - endif() -endif() diff --git a/packages/HighFive/deps/catch2 b/packages/HighFive/deps/catch2 index 4e8d92bf02f7d1c8006a0e7a5ecabd8e62d98502..fa43b77429ba76c462b1898d6cd2f2d7a9416b14 160000 --- a/packages/HighFive/deps/catch2 +++ b/packages/HighFive/deps/catch2 @@ -1 +1 @@ -Subproject commit 4e8d92bf02f7d1c8006a0e7a5ecabd8e62d98502 +Subproject commit fa43b77429ba76c462b1898d6cd2f2d7a9416b14 diff --git a/packages/HighFive/doc/developer_guide.md b/packages/HighFive/doc/developer_guide.md index 4537bef20bb87a6d78dbaa4d78d3ae2d387b4741..45e86b0f4c1cc55b7f122e90453a174951af64bb 100644 --- a/packages/HighFive/doc/developer_guide.md +++ b/packages/HighFive/doc/developer_guide.md @@ -41,22 +41,16 @@ tests. ### Code formatting The project is formatted using clang-format version 12.0.1 and CI will complain if a commit isn't formatted accordingly. The `.clang-format` is at the root of -the git repository. Conveniently, `clang-format` is available via `pip`: +the git repository. Conveniently, `clang-format` is available via `pip`. +Formatting the entire code base can be done with: ```bash -python -m venv venv -source venv/bin/activate - -pip install clang-format==12.0.1 + bin/format.sh ``` +which will install the required version of clang-format in a venv called +`.clang-format-venv`. -The changed lines can be formatted with `git-clang-format`, e.g. to format all lines changed compared to master: - -```bash -git-clang-format master -``` -(add `-f` to allow formatting unstaged changes if you trust it to not destroy -your changes.) +To format only the changed files `git-clang-format` can be used. ## Releasing HighFive Before releasing a new version perform the following: diff --git a/packages/HighFive/doc/doxygen-awesome-css/doxygen-awesome.css b/packages/HighFive/doc/doxygen-awesome-css/doxygen-awesome.css index a2715e268c5532e94abf3d4a2591395932438e22..c2f41142f995a0fcbe27526264725e1b3cdbb65d 100644 --- a/packages/HighFive/doc/doxygen-awesome-css/doxygen-awesome.css +++ b/packages/HighFive/doc/doxygen-awesome-css/doxygen-awesome.css @@ -805,6 +805,8 @@ html.dark-mode iframe#MSearchResults { #nav-tree .item { height: var(--tree-item-height); line-height: var(--tree-item-height); + overflow: hidden; + text-overflow: ellipsis; } #nav-tree .item > a:focus { @@ -823,6 +825,8 @@ html.dark-mode iframe#MSearchResults { background-image: none; background-color: transparent; position: relative; + color: var(--primary-color) !important; + font-weight: 500; } #nav-tree .selected::after { @@ -1749,7 +1753,7 @@ table.fieldtable th { color: var(--tablehead-foreground); } -table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fielddoc, .fieldtable th { +table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fieldinit, .fieldtable td.fielddoc, .fieldtable th { border-bottom: 1px solid var(--separator-color); border-right: 1px solid var(--separator-color); } diff --git a/packages/HighFive/doc/migration_guide.md b/packages/HighFive/doc/migration_guide.md index ee72e52c7ad00b3eec394727e4f7fa99b73a0dfb..b8ab0ec4e2d71ebd93a57f21df40e46cb3d942f3 100644 --- a/packages/HighFive/doc/migration_guide.md +++ b/packages/HighFive/doc/migration_guide.md @@ -133,7 +133,7 @@ target_link_libraries(add PUBLIC boost::boost) # For HighFive there's two options for adding `-I ${HIGHFIVE_DIR}` and the # flags for HDF5. # -# Option 1: HighFive is install (systemwide) as a regular library: +# Option 1: HighFive is installed as a (system-wide) regular library: find_package(HighFive) target_link_libraries(app PUBLIC HighFive::HighFive) @@ -169,7 +169,7 @@ of a "file driver". Removing the concept hopefully makes it easier to add a better abstraction for the handling of the property lists, when we discover such an abstraction. -## Removal of broadcasting +## Removal of broadcasting. HighFive v2 had a feature that a dataset (or attribute) of shape `[n, 1]` could be read into a one-dimensional array automatically. @@ -233,6 +233,32 @@ multi-dimensional array isn't supported, because if we want to support array with runtime-defined rank, we can't deduce the correct shape, e.g. `[1]` vs. `[1, 1, 1]`, when read into an array. -# Removal of `Object*Props`. -To out knowledge these could not be used meaningfully. Please create an issue +## Change to `File::Truncate` and friends. +In `v2`, `File::{ReadOnly,Truncate,...}` was an anonymous member enum of +`File`. Effectively it's type was the same as an `int`. + +To improve type-safety, we converted it into an `enum class` called +`File::AccessMode`. In order to reduce the migration effort, we retained the +ability to write: `File::ReadOnly`. + +Functions that accept a file access mode should be modernized as follows: +``` +// old +HighFive::File open(std::string name, int mode) { + return HighFive::File(name, mode); +} + +// new +HighFive::File open(std::string name, HighFive::File::AccessMode mode) { + return HighFive::File(name, mode); +} +``` + +Note: There's a caveat, the short-hand notation `File::ReadOnly` doesn't have +an address. Meaning one can't take it's address or const-references of it +(results in a linker error about missing symbol `File::ReadOnly`). Use +`File::AccessMode::ReadOnly` instead. + +## Removal of `Object*Props`. +To our knowledge these could not be used meaningfully. Please create an issue if you relied on these. diff --git a/packages/HighFive/include/highfive/H5DataSpace.hpp b/packages/HighFive/include/highfive/H5DataSpace.hpp index 463648507a9fea2c7ffd27cbca1230348b4c3385..6c98d4c9903881d85b04cdca599c237130ff2e20 100644 --- a/packages/HighFive/include/highfive/H5DataSpace.hpp +++ b/packages/HighFive/include/highfive/H5DataSpace.hpp @@ -19,6 +19,14 @@ namespace HighFive { +namespace detail { +/// @brief Create a HighFive::DataSpace from an HID, without incrementing the id. +/// +/// @note This is internal API and subject to change. +/// @internal +DataSpace make_data_space(hid_t hid); +} // namespace detail + /// \brief Class representing the space (dimensions) of a DataSet /// /// \code{.cpp} @@ -254,9 +262,18 @@ class DataSpace: public Object { protected: DataSpace() = default; + static DataSpace fromId(hid_t hid) { + DataSpace space; + space._hid = hid; + + return space; + } + friend class Attribute; friend class File; friend class DataSet; + + friend DataSpace detail::make_data_space(hid_t hid); }; } // namespace HighFive diff --git a/packages/HighFive/include/highfive/H5DataType.hpp b/packages/HighFive/include/highfive/H5DataType.hpp index 985eb6dd457fea7037df20386a6e553e8ec3b4ee..b7e7d78e173f2d947ea96657c979b5dc1da1885c 100644 --- a/packages/HighFive/include/highfive/H5DataType.hpp +++ b/packages/HighFive/include/highfive/H5DataType.hpp @@ -153,16 +153,18 @@ class FixedLengthStringType: public StringType { /// UTF8. In particular, a string with `n` UFT8 characters in general /// requires `4*n` bytes. /// - /// The string padding is subtle, essentially it's just a hint. A - /// null-terminated string is guaranteed to have one `'\0'` which marks the - /// semantic end of the string. The length of the buffer must be at least - /// `size` bytes regardless. HDF5 will read or write `size` bytes, - /// irrespective of the when the `\0` occurs. + /// The string padding is subtle, essentially it's just a hint. While + /// commonly, a null-terminated string is guaranteed to have one `'\0'` + /// which marks the semantic end of the string, this is not enforced by + /// HDF5. In fact, there are HDF5 files that contain strings that claim to + /// be null-terminated but aren't. The length of the buffer must be at + /// least `size` bytes regardless of the padding. HDF5 will read or write + /// `size` bytes, irrespective of when (if at all) the `\0` occurs. /// - /// Note that when writing passing `StringPadding::NullTerminated` is a + /// Note that when writing, passing `StringPadding::NullTerminated` is a /// guarantee to the reader that it contains a `\0`. Therefore, make sure - /// that the string really is nullterminated. Otherwise prefer a - /// null-padded string which only means states that the buffer is filled up + /// that the string really is null-terminated. Otherwise prefer a + /// null-padded string. This mearly states that the buffer is filled up /// with 0 or more `\0`. FixedLengthStringType(size_t size, StringPadding padding, diff --git a/packages/HighFive/include/highfive/H5File.hpp b/packages/HighFive/include/highfive/H5File.hpp index b134aaa49dc2131688acf7133c4589c2e5dba748..fd77042b05a31413e77f8a93269cea049db10ac3 100644 --- a/packages/HighFive/include/highfive/H5File.hpp +++ b/packages/HighFive/include/highfive/H5File.hpp @@ -9,6 +9,7 @@ #pragma once #include <string> +#include <type_traits> #include "H5Object.hpp" #include "H5PropertyList.hpp" @@ -17,6 +18,7 @@ namespace HighFive { + /// /// \brief File class /// @@ -24,25 +26,35 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File> public: const static ObjectType type = ObjectType::File; - enum : unsigned { + enum class AccessMode { + None = 0x00u, /// Open flag: Read only access - ReadOnly = 0x00u, + ReadOnly = 0x01u, /// Open flag: Read Write access - ReadWrite = 0x01u, + ReadWrite = 0x02u, /// Open flag: Truncate a file if already existing - Truncate = 0x02u, + Truncate = 0x04u, /// Open flag: Open will fail if file already exist - Excl = 0x04u, + Excl = 0x08u, /// Open flag: Open in debug mode - Debug = 0x08u, + Debug = 0x10u, /// Open flag: Create non existing file - Create = 0x10u, + Create = 0x20u, /// Derived open flag: common write mode (=ReadWrite|Create|Truncate) Overwrite = Truncate, /// Derived open flag: Opens RW or exclusively creates OpenOrCreate = ReadWrite | Create }; + constexpr static AccessMode ReadOnly = AccessMode::ReadOnly; + constexpr static AccessMode ReadWrite = AccessMode::ReadWrite; + constexpr static AccessMode Truncate = AccessMode::Truncate; + constexpr static AccessMode Excl = AccessMode::Excl; + constexpr static AccessMode Debug = AccessMode::Debug; + constexpr static AccessMode Create = AccessMode::Create; + constexpr static AccessMode Overwrite = AccessMode::Overwrite; + constexpr static AccessMode OpenOrCreate = AccessMode::OpenOrCreate; + /// /// \brief File /// \param filename: filepath of the HDF5 file @@ -51,7 +63,7 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File> /// /// Open or create a new HDF5 file explicit File(const std::string& filename, - unsigned openFlags = ReadOnly, + AccessMode openFlags = ReadOnly, const FileAccessProps& fileAccessProps = FileAccessProps::Default()); /// @@ -63,7 +75,7 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File> /// /// Open or create a new HDF5 file File(const std::string& filename, - unsigned openFlags, + AccessMode openFlags, const FileCreateProps& fileCreateProps, const FileAccessProps& fileAccessProps = FileAccessProps::Default()); @@ -131,6 +143,46 @@ class File: public Object, public NodeTraits<File>, public AnnotateTraits<File> friend class PathTraits; }; +inline File::AccessMode operator|(File::AccessMode lhs, File::AccessMode rhs) { + using int_t = std::underlying_type<File::AccessMode>::type; + return static_cast<File::AccessMode>(static_cast<int_t>(lhs) | static_cast<int_t>(rhs)); +} + +inline File::AccessMode operator&(File::AccessMode lhs, File::AccessMode rhs) { + using int_t = std::underlying_type<File::AccessMode>::type; + return static_cast<File::AccessMode>(static_cast<int_t>(lhs) & static_cast<int_t>(rhs)); +} + +inline File::AccessMode operator^(File::AccessMode lhs, File::AccessMode rhs) { + using int_t = std::underlying_type<File::AccessMode>::type; + return static_cast<File::AccessMode>(static_cast<int_t>(lhs) ^ static_cast<int_t>(rhs)); +} + +inline File::AccessMode operator~(File::AccessMode mode) { + using int_t = std::underlying_type<File::AccessMode>::type; + return static_cast<File::AccessMode>(~static_cast<int_t>(mode)); +} + +inline const File::AccessMode& operator|=(File::AccessMode& lhs, File::AccessMode rhs) { + lhs = lhs | rhs; + return lhs; +} + +inline File::AccessMode operator&=(File::AccessMode& lhs, File::AccessMode rhs) { + lhs = lhs & rhs; + return lhs; +} + +inline File::AccessMode operator^=(File::AccessMode& lhs, File::AccessMode rhs) { + lhs = lhs ^ rhs; + return lhs; +} + +inline bool any(File::AccessMode mode) { + return mode != File::AccessMode::None; +} + + } // namespace HighFive // H5File is the main user constructible -> bring in implementation headers diff --git a/packages/HighFive/include/highfive/H5Group.hpp b/packages/HighFive/include/highfive/H5Group.hpp index eebfcbb3ee0d0789c7d1e87bc26491e6ac3530da..5c09069dee751888740739fe1ad141d1bf80e155 100644 --- a/packages/HighFive/include/highfive/H5Group.hpp +++ b/packages/HighFive/include/highfive/H5Group.hpp @@ -57,7 +57,7 @@ class Group: public Object, } Group(Object&& o) noexcept - : Object(std::move(o)){}; + : Object(std::move(o)) {}; protected: using Object::Object; diff --git a/packages/HighFive/include/highfive/H5Reference.hpp b/packages/HighFive/include/highfive/H5Reference.hpp index 38062e987b73dc64954c733f8873da027b8c4dd3..56d1bdd9cc5a60d88914ada7263068553a30fcf3 100644 --- a/packages/HighFive/include/highfive/H5Reference.hpp +++ b/packages/HighFive/include/highfive/H5Reference.hpp @@ -58,7 +58,7 @@ class Reference { protected: /// \brief Create a Reference from a low-level HDF5 object reference inline explicit Reference(const hobj_ref_t h5_ref) - : href(h5_ref){}; + : href(h5_ref) {}; /// \brief Create the low-level reference and store it at refptr /// diff --git a/packages/HighFive/include/highfive/H5Version.hpp b/packages/HighFive/include/highfive/H5Version.hpp index 4ffb036452953562b86ff2962de9045ccf518482..f8dca4e2fc6287001166c361b0e89b8781c97228 100644 --- a/packages/HighFive/include/highfive/H5Version.hpp +++ b/packages/HighFive/include/highfive/H5Version.hpp @@ -12,6 +12,9 @@ #define HIGHFIVE_VERSION_MINOR 0 #define HIGHFIVE_VERSION_PATCH 0 +// Undefined for regular releases. +#define HIGHFIVE_VERSION_PRERELEASE 2 + /** \brief Concatenated representation of the HighFive version. * * \warning The macro `HIGHFIVE_VERSION` by itself isn't valid C/C++. @@ -30,4 +33,4 @@ * * \warning This macro only exists from 2.7.1 onwards. */ -#define HIGHFIVE_VERSION_STRING "3.0.0" +#define HIGHFIVE_VERSION_STRING "3.0.0-beta2" diff --git a/packages/HighFive/include/highfive/H5Version.hpp.in b/packages/HighFive/include/highfive/H5Version.hpp.in index acddcffd39407ea485e4c629384ef8c572b79586..bb7eda58eb6c9cbbe7ecad975e1b463340786390 100644 --- a/packages/HighFive/include/highfive/H5Version.hpp.in +++ b/packages/HighFive/include/highfive/H5Version.hpp.in @@ -12,6 +12,9 @@ #define HIGHFIVE_VERSION_MINOR @PROJECT_VERSION_MINOR@ #define HIGHFIVE_VERSION_PATCH @PROJECT_VERSION_PATCH@ +// Undefined for regular releases. +#define HIGHFIVE_VERSION_PRERELEASE @HIGHFIVE_VERSION_PRERELEASE@ + /** \brief Concatenated representation of the HighFive version. * * \warning The macro `HIGHFIVE_VERSION` by itself isn't valid C/C++. @@ -30,4 +33,4 @@ * * \warning This macro only exists from 2.7.1 onwards. */ -#define HIGHFIVE_VERSION_STRING "@PROJECT_VERSION@" +#define HIGHFIVE_VERSION_STRING "@PROJECT_VERSION@-beta@HIGHFIVE_VERSION_PRERELEASE@" diff --git a/packages/HighFive/include/highfive/bits/H5Converter_misc.hpp b/packages/HighFive/include/highfive/bits/H5Converter_misc.hpp index 5fcbafb5e64308a4560ffa17be2b3b6bbe183fd6..b03d3bec46ec8cc5f144636ed5519a526ad03a16 100644 --- a/packages/HighFive/include/highfive/bits/H5Converter_misc.hpp +++ b/packages/HighFive/include/highfive/bits/H5Converter_misc.hpp @@ -45,7 +45,7 @@ struct ShallowCopyBuffer { ShallowCopyBuffer() = delete; explicit ShallowCopyBuffer(typename std::conditional<IsReadOnly, const T&, T&>::type val) - : ptr(inspector<T>::data(val)){}; + : ptr(inspector<T>::data(val)) {}; hdf5_type* getPointer() const { return ptr; @@ -103,14 +103,14 @@ enum class BufferMode { Read, Write }; /// /// \brief String length in bytes excluding the `\0`. /// -inline size_t char_buffer_size(char const* const str, size_t max_string_length) { - for (size_t i = 0; i <= max_string_length; ++i) { +inline size_t char_buffer_length(char const* const str, size_t max_string_size) { + for (size_t i = 0; i < max_string_size; ++i) { if (str[i] == '\0') { return i; } } - return max_string_length; + return max_string_size; } @@ -190,7 +190,7 @@ struct StringBuffer { } else if (buffer.isFixedLengthString()) { // If the buffer is fixed-length and null-terminated, then // `buffer.string_length` doesn't include the null-character. - if (length > buffer.string_length) { + if (length > buffer.string_max_length) { throw std::invalid_argument("String length too big."); } @@ -229,9 +229,9 @@ struct StringBuffer { /// `length() + 1` bytes long. size_t length() const { if (buffer.isNullTerminated()) { - return char_buffer_size(data(), buffer.string_length); + return char_buffer_length(data(), buffer.string_size); } else { - return buffer.string_length; + return buffer.string_max_length; } } @@ -272,7 +272,7 @@ struct StringBuffer { : file_datatype(_file_datatype.asStringType()) , padding(file_datatype.getPadding()) , string_size(file_datatype.isVariableStr() ? size_t(-1) : file_datatype.getSize()) - , string_length(string_size - size_t(isNullTerminated())) + , string_max_length(string_size - size_t(isNullTerminated())) , dims(_dims) { if (string_size == 0 && isNullTerminated()) { throw DataTypeException( @@ -322,9 +322,11 @@ struct StringBuffer { private: StringType file_datatype; StringPadding padding; - size_t string_size; // Size of buffer required to store the string. - // Meaningful for fixed length strings only. - size_t string_length; // Semantic length of string. + // Size of buffer required to store the string. + // Meaningful for fixed length strings only. + size_t string_size; + // Maximum length of string. + size_t string_max_length; std::vector<size_t> dims; std::vector<char> fixed_length_buffer; @@ -347,7 +349,7 @@ struct Writer<T, typename enable_shallow_copy<T>::type>: public ShallowCopyBuffe explicit Writer(const T& val, const std::vector<size_t>& /* dims */, const DataType& /* file_datatype */) - : super(val){}; + : super(val) {}; }; template <typename T> diff --git a/packages/HighFive/include/highfive/bits/H5Dataspace_misc.hpp b/packages/HighFive/include/highfive/bits/H5Dataspace_misc.hpp index 4382c14c1305fcf989f641ab99116b059116e37e..21ec6397eb09331bd1ea5f97842bba1c0884b6a1 100644 --- a/packages/HighFive/include/highfive/bits/H5Dataspace_misc.hpp +++ b/packages/HighFive/include/highfive/bits/H5Dataspace_misc.hpp @@ -21,6 +21,12 @@ namespace HighFive { +namespace detail { +inline DataSpace make_data_space(hid_t hid) { + return DataSpace::fromId(hid); +} +} // namespace detail + inline DataSpace::DataSpace(const std::vector<size_t>& dims) : DataSpace(dims.begin(), dims.end()) {} diff --git a/packages/HighFive/include/highfive/bits/H5File_misc.hpp b/packages/HighFive/include/highfive/bits/H5File_misc.hpp index 6013953b13e5433c3ef5c4dfeaf9eae4e1c96930..0d8088da514579d7221b082c8cbc95a2d8ea083d 100644 --- a/packages/HighFive/include/highfive/bits/H5File_misc.hpp +++ b/packages/HighFive/include/highfive/bits/H5File_misc.hpp @@ -22,33 +22,33 @@ namespace { // unnamed // libhdf5 uses a preprocessor trick on their oflags // we can not declare them constant without a mapper -inline unsigned convert_open_flag(unsigned openFlags) { +inline unsigned convert_open_flag(File::AccessMode openFlags) { unsigned res_open = 0; - if (openFlags & File::ReadOnly) + if (any(openFlags & File::ReadOnly)) res_open |= H5F_ACC_RDONLY; - if (openFlags & File::ReadWrite) + if (any(openFlags & File::ReadWrite)) res_open |= H5F_ACC_RDWR; - if (openFlags & File::Create) + if (any(openFlags & File::Create)) res_open |= H5F_ACC_CREAT; - if (openFlags & File::Truncate) + if (any(openFlags & File::Truncate)) res_open |= H5F_ACC_TRUNC; - if (openFlags & File::Excl) + if (any(openFlags & File::Excl)) res_open |= H5F_ACC_EXCL; return res_open; } } // namespace inline File::File(const std::string& filename, - unsigned openFlags, + AccessMode openFlags, const FileAccessProps& fileAccessProps) : File(filename, openFlags, FileCreateProps::Default(), fileAccessProps) {} inline File::File(const std::string& filename, - unsigned openFlags, + AccessMode access_mode, const FileCreateProps& fileCreateProps, const FileAccessProps& fileAccessProps) { - openFlags = convert_open_flag(openFlags); + unsigned openFlags = convert_open_flag(access_mode); unsigned createMode = openFlags & (H5F_ACC_TRUNC | H5F_ACC_EXCL); unsigned openMode = openFlags & (H5F_ACC_RDWR | H5F_ACC_RDONLY); diff --git a/packages/HighFive/include/highfive/bits/H5Slice_traits.hpp b/packages/HighFive/include/highfive/bits/H5Slice_traits.hpp index 6812a091449ee5aa7c3343a439b123171fa56f8c..b38c8ec772bd6e4dde07717fef466043053c8405 100644 --- a/packages/HighFive/include/highfive/bits/H5Slice_traits.hpp +++ b/packages/HighFive/include/highfive/bits/H5Slice_traits.hpp @@ -162,20 +162,7 @@ class HyperSlab { } DataSpace apply(const DataSpace& space_) const { - auto space = space_.clone(); - for (const auto& sel: selects) { - if (sel.op == Op::None) { - detail::h5s_select_none(space.getId()); - } else { - detail::h5s_select_hyperslab(space.getId(), - convert(sel.op), - sel.offset.empty() ? nullptr : sel.offset.data(), - sel.stride.empty() ? nullptr : sel.stride.data(), - sel.count.empty() ? nullptr : sel.count.data(), - sel.block.empty() ? nullptr : sel.block.data()); - } - } - return space; + return apply_impl(space_); } private: @@ -229,6 +216,124 @@ class HyperSlab { }; std::vector<Select_> selects; + + protected: + DataSpace select_none(const DataSpace& outer_space) const { + auto space = outer_space.clone(); + detail::h5s_select_none(space.getId()); + return space; + } + + void select_hyperslab(DataSpace& space, const Select_& sel) const { + detail::h5s_select_hyperslab(space.getId(), + convert(sel.op), + sel.offset.empty() ? nullptr : sel.offset.data(), + sel.stride.empty() ? nullptr : sel.stride.data(), + sel.count.empty() ? nullptr : sel.count.data(), + sel.block.empty() ? nullptr : sel.block.data()); + } + +#if H5_VERSION_GE(1, 10, 6) + /// The length of a stream of `Op::Or` starting at `begin`. + size_t detect_streak(Select_ const* begin, Select_ const* end, Op op) const { + assert(op == Op::Or); + auto it = std::find_if(begin, end, [op](const Select_& sel) { return sel.op != op; }); + return static_cast<size_t>(it - begin); + } + + DataSpace combine_selections(const DataSpace& left_space, + Op op, + const DataSpace& right_space) const { + assert(op == Op::Or); + + auto left_type = detail::h5s_get_select_type(left_space.getId()); + auto right_type = detail::h5s_get_select_type(right_space.getId()); + + // Since HDF5 doesn't allow `combine_selections` with a None + // selection, we need to avoid the issue: + if (left_type == H5S_SEL_NONE) { + return right_space; + } else if (right_type == H5S_SEL_NONE) { + return left_space; + } else if (left_type == H5S_SEL_ALL) { + return left_space; + } else if (right_type == H5S_SEL_ALL) { + return right_space; + } else { + return detail::make_data_space( + detail::h5s_combine_select(left_space.getId(), convert(op), right_space.getId())); + } + } + + /// Reduce a sequence of `Op::Or` efficiently. + /// + /// The issue is that `H5Sselect_hyperslab` runs in time that linear of the + /// number of block in the existing selection. Therefore, a loop that adds + /// slab-by-slab has quadratic runtime in the number of slabs. + /// + /// Fortunately, `H5Scombine_select` doesn't suffer from the same problem. + /// However, it's only available in 1.10.6 and newer. + /// + /// The solution is to use divide-and-conquer to reduce (long) streaks of + /// `Op::Or` in what seems to be log-linear time. + DataSpace reduce_streak(const DataSpace& outer_space, + Select_ const* begin, + Select_ const* end, + Op op) const { + assert(op == Op::Or); + + if (begin == end) { + throw std::runtime_error("Broken logic in 'DataSpace::reduce_streak'."); + } + + std::ptrdiff_t distance = end - begin; + if (distance == 1) { + auto space = select_none(outer_space); + select_hyperslab(space, *begin); + return space; + } + + Select_ const* mid = begin + distance / 2; + auto right_space = reduce_streak(outer_space, begin, mid, op); + auto left_space = reduce_streak(outer_space, mid, end, op); + + return combine_selections(left_space, op, right_space); + } + + DataSpace apply_impl(const DataSpace& space_) const { + auto space = space_.clone(); + auto n_selects = selects.size(); + for (size_t i = 0; i < n_selects; ++i) { + auto begin = selects.data() + i; + auto end = selects.data() + n_selects; + + auto n_ors = detect_streak(begin, end, Op::Or); + + if (n_ors > 1) { + auto right_space = reduce_streak(space_, begin, begin + n_ors, Op::Or); + space = combine_selections(space, Op::Or, right_space); + i += n_ors - 1; + } else if (selects[i].op == Op::None) { + detail::h5s_select_none(space.getId()); + } else { + select_hyperslab(space, selects[i]); + } + } + return space; + } +#else + DataSpace apply_impl(const DataSpace& space_) const { + auto space = space_.clone(); + for (const auto& sel: selects) { + if (sel.op == Op::None) { + detail::h5s_select_none(space.getId()); + } else { + select_hyperslab(space, sel); + } + } + return space; + } +#endif }; /// @@ -296,6 +401,7 @@ class HyperSlab { /// /// Note that the selections along each axis must be sorted and non-overlapping. /// +/// \since 3.0 class ProductSet { public: template <class... Slices> @@ -356,6 +462,11 @@ class SliceTraits { /// Selection select(const ElementSet& elements) const; + /// + /// \brief Select a region consisting of a product of slices. + /// + /// \since 3.0 + /// Selection select(const ProductSet& product_set) const; template <typename T> diff --git a/packages/HighFive/include/highfive/bits/h5s_wrapper.hpp b/packages/HighFive/include/highfive/bits/h5s_wrapper.hpp index 03edf80058de0340e19e48a35e2f804f338134b1..250ffcf6de78b51bc501836f276dc50e384ad39c 100644 --- a/packages/HighFive/include/highfive/bits/h5s_wrapper.hpp +++ b/packages/HighFive/include/highfive/bits/h5s_wrapper.hpp @@ -1,5 +1,6 @@ #pragma once +#include <H5Ipublic.h> #include <H5Spublic.h> namespace HighFive { namespace detail { @@ -110,6 +111,26 @@ inline H5S_class_t h5s_get_simple_extent_type(hid_t space_id) { return cls; } +inline H5S_sel_type h5s_get_select_type(hid_t space_id) { + H5S_sel_type type = H5Sget_select_type(space_id); + if (type < 0) { + HDF5ErrMapper::ToException<DataSpaceException>("Unable to get type of selection."); + } + + return type; +} + +#if H5_VERSION_GE(1, 10, 6) +inline hid_t h5s_combine_select(hid_t space1_id, H5S_seloper_t op, hid_t space2_id) { + auto space_id = H5Scombine_select(space1_id, op, space2_id); + if (space_id == H5I_INVALID_HID) { + HDF5ErrMapper::ToException<DataSpaceException>("Unable to combine two selections."); + } + + return space_id; +} +#endif + } // namespace detail } // namespace HighFive diff --git a/packages/HighFive/include/highfive/bits/inspector_stl_span_misc.hpp b/packages/HighFive/include/highfive/bits/inspector_stl_span_misc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..226be4dc029d874179d328098e526afdd740594d --- /dev/null +++ b/packages/HighFive/include/highfive/bits/inspector_stl_span_misc.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include "H5Inspector_decl.hpp" +#include "../H5Exception.hpp" + +#include <cstdlib> +#include <vector> +#include <type_traits> + +namespace HighFive { +namespace details { + + +// Anything with the same API as `std::span` can implemented by inheriting from +// this class. +template <class Span> +struct inspector_stl_span { + using type = Span; + using value_type = unqualified_t<typename Span::value_type>; + using base_type = typename inspector<value_type>::base_type; + using hdf5_type = typename inspector<value_type>::hdf5_type; + + static constexpr size_t ndim = 1; + static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim; + static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim; + + static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value && + inspector<value_type>::is_trivially_nestable; + static constexpr bool is_trivially_nestable = false; + + + static size_t getRank(const type& val) { + if (!val.empty()) { + return ndim + inspector<value_type>::getRank(val[0]); + } else { + return min_ndim; + } + } + + static std::vector<size_t> getDimensions(const type& val) { + auto rank = getRank(val); + std::vector<size_t> sizes(rank, 1ul); + sizes[0] = val.size(); + if (!val.empty()) { + auto s = inspector<value_type>::getDimensions(val[0]); + assert(s.size() + ndim == sizes.size()); + for (size_t i = 0; i < s.size(); ++i) { + sizes[i + ndim] = s[i]; + } + } + return sizes; + } + + static void prepare(type& val, const std::vector<size_t>& expected_dims) { + auto actual_dims = getDimensions(val); + if (actual_dims.size() != expected_dims.size()) { + throw DataSpaceException("Mismatching rank."); + } + + for (size_t i = 0; i < actual_dims.size(); ++i) { + if (actual_dims[i] != expected_dims[i]) { + throw DataSpaceException("Mismatching dimensions."); + } + } + } + + static hdf5_type* data(type& val) { + return val.empty() ? nullptr : inspector<value_type>::data(val[0]); + } + + static const hdf5_type* data(const type& val) { + return val.empty() ? nullptr : inspector<value_type>::data(val[0]); + } + + template <class It> + static void serialize(const type& val, const std::vector<size_t>& dims, It m) { + if (!val.empty()) { + auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(subdims); + for (const auto& e: val) { + inspector<value_type>::serialize(e, subdims, m); + m += subsize; + } + } + } + + template <class It> + static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) { + std::vector<size_t> subdims(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(subdims); + for (size_t i = 0; i < dims[0]; ++i) { + inspector<value_type>::unserialize(vec_align + i * subsize, subdims, val[i]); + } + } +}; + +} // namespace details +} // namespace HighFive diff --git a/packages/HighFive/include/highfive/boost.hpp b/packages/HighFive/include/highfive/boost.hpp index 33c1458df0735c4c7061196a7b417f93c6050139..64a67c67b5375c9f89d0722f6fc0630ae864af7e 100644 --- a/packages/HighFive/include/highfive/boost.hpp +++ b/packages/HighFive/include/highfive/boost.hpp @@ -1,176 +1,4 @@ #pragma once -#include "bits/H5Inspector_decl.hpp" -#include "H5Exception.hpp" - -#include <boost/multi_array.hpp> -#include <boost/numeric/ublas/matrix.hpp> - -namespace HighFive { -namespace details { - -template <typename T, size_t Dims> -struct inspector<boost::multi_array<T, Dims>> { - using type = boost::multi_array<T, Dims>; - using value_type = T; - using base_type = typename inspector<value_type>::base_type; - using hdf5_type = typename inspector<value_type>::hdf5_type; - - static constexpr size_t ndim = Dims; - static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim; - static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim; - - static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value && - inspector<value_type>::is_trivially_nestable; - static constexpr bool is_trivially_nestable = false; - - - static size_t getRank(const type& val) { - return ndim + inspector<value_type>::getRank(val.data()[0]); - } - - static std::vector<size_t> getDimensions(const type& val) { - auto rank = getRank(val); - std::vector<size_t> sizes(rank, 1ul); - for (size_t i = 0; i < ndim; ++i) { - sizes[i] = val.shape()[i]; - } - if (val.size() != 0) { - auto s = inspector<value_type>::getDimensions(val.data()[0]); - sizes.resize(ndim + s.size()); - for (size_t i = 0; i < s.size(); ++i) { - sizes[ndim + i] = s[i]; - } - } - return sizes; - } - - static void prepare(type& val, const std::vector<size_t>& dims) { - if (dims.size() < ndim) { - std::ostringstream os; - os << "Only '" << dims.size() << "' given but boost::multi_array is of size '" << ndim - << "'."; - throw DataSpaceException(os.str()); - } - boost::array<typename type::index, Dims> ext; - std::copy(dims.begin(), dims.begin() + ndim, ext.begin()); - val.resize(ext); - std::vector<size_t> next_dims(dims.begin() + Dims, dims.end()); - std::size_t size = std::accumulate(dims.begin(), - dims.begin() + Dims, - std::size_t{1}, - std::multiplies<size_t>()); - for (size_t i = 0; i < size; ++i) { - inspector<value_type>::prepare(*(val.origin() + i), next_dims); - } - } - - static void assert_c_order(const type& val) { - if (!(val.storage_order() == boost::c_storage_order())) { - throw DataTypeException("Only C storage order is supported for 'boost::multi_array'."); - } - } - - static hdf5_type* data(type& val) { - assert_c_order(val); - return inspector<value_type>::data(*val.data()); - } - - static const hdf5_type* data(const type& val) { - assert_c_order(val); - return inspector<value_type>::data(*val.data()); - } - - template <class It> - static void serialize(const type& val, const std::vector<size_t>& dims, It m) { - assert_c_order(val); - size_t size = val.num_elements(); - auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(subdims); - for (size_t i = 0; i < size; ++i) { - inspector<value_type>::serialize(*(val.origin() + i), subdims, m + i * subsize); - } - } - - template <class It> - static void unserialize(It vec_align, const std::vector<size_t>& dims, type& val) { - assert_c_order(val); - std::vector<size_t> next_dims(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(next_dims); - for (size_t i = 0; i < val.num_elements(); ++i) { - inspector<value_type>::unserialize(vec_align + i * subsize, - next_dims, - *(val.origin() + i)); - } - } -}; - -template <typename T> -struct inspector<boost::numeric::ublas::matrix<T>> { - using type = boost::numeric::ublas::matrix<T>; - using value_type = unqualified_t<T>; - using base_type = typename inspector<value_type>::base_type; - using hdf5_type = typename inspector<value_type>::hdf5_type; - - static constexpr size_t ndim = 2; - static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim; - static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim; - - static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value && - inspector<value_type>::is_trivially_copyable; - static constexpr bool is_trivially_nestable = false; - - static size_t getRank(const type& val) { - return ndim + inspector<value_type>::getRank(val(0, 0)); - } - - static std::vector<size_t> getDimensions(const type& val) { - std::vector<size_t> sizes{val.size1(), val.size2()}; - auto s = inspector<value_type>::getDimensions(val(0, 0)); - sizes.insert(sizes.end(), s.begin(), s.end()); - return sizes; - } - - static void prepare(type& val, const std::vector<size_t>& dims) { - if (dims.size() < ndim) { - std::ostringstream os; - os << "Impossible to pair DataSet with " << dims.size() << " dimensions into a " << ndim - << " boost::numeric::ublas::matrix"; - throw DataSpaceException(os.str()); - } - val.resize(dims[0], dims[1], false); - } - - static hdf5_type* data(type& val) { - return inspector<value_type>::data(val(0, 0)); - } - - static const hdf5_type* data(const type& val) { - return inspector<value_type>::data(val(0, 0)); - } - - static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* m) { - size_t size = val.size1() * val.size2(); - auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(subdims); - for (size_t i = 0; i < size; ++i) { - inspector<value_type>::serialize(*(&val(0, 0) + i), subdims, m + i * subsize); - } - } - - static void unserialize(const hdf5_type* vec_align, - const std::vector<size_t>& dims, - type& val) { - std::vector<size_t> next_dims(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(next_dims); - size_t size = val.size1() * val.size2(); - for (size_t i = 0; i < size; ++i) { - inspector<value_type>::unserialize(vec_align + i * subsize, - next_dims, - *(&val(0, 0) + i)); - } - } -}; - -} // namespace details -} // namespace HighFive +#include "boost_ublas.hpp" +#include "boost_multi_array.hpp" diff --git a/packages/HighFive/include/highfive/boost_multi_array.hpp b/packages/HighFive/include/highfive/boost_multi_array.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5a07d973c571a5bdc28d6d0e3f34b75c19db0213 --- /dev/null +++ b/packages/HighFive/include/highfive/boost_multi_array.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include "bits/H5Inspector_decl.hpp" +#include "H5Exception.hpp" + +#include <boost/multi_array.hpp> + +namespace HighFive { +namespace details { + +template <typename T, size_t Dims> +struct inspector<boost::multi_array<T, Dims>> { + using type = boost::multi_array<T, Dims>; + using value_type = T; + using base_type = typename inspector<value_type>::base_type; + using hdf5_type = typename inspector<value_type>::hdf5_type; + + static constexpr size_t ndim = Dims; + static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim; + static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim; + + static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value && + inspector<value_type>::is_trivially_nestable; + static constexpr bool is_trivially_nestable = false; + + + static size_t getRank(const type& val) { + return ndim + inspector<value_type>::getRank(val.data()[0]); + } + + static std::vector<size_t> getDimensions(const type& val) { + auto rank = getRank(val); + std::vector<size_t> sizes(rank, 1ul); + for (size_t i = 0; i < ndim; ++i) { + sizes[i] = val.shape()[i]; + } + if (val.size() != 0) { + auto s = inspector<value_type>::getDimensions(val.data()[0]); + sizes.resize(ndim + s.size()); + for (size_t i = 0; i < s.size(); ++i) { + sizes[ndim + i] = s[i]; + } + } + return sizes; + } + + static void prepare(type& val, const std::vector<size_t>& dims) { + if (dims.size() < ndim) { + std::ostringstream os; + os << "Only '" << dims.size() << "' given but boost::multi_array is of size '" << ndim + << "'."; + throw DataSpaceException(os.str()); + } + boost::array<typename type::index, Dims> ext; + std::copy(dims.begin(), dims.begin() + ndim, ext.begin()); + val.resize(ext); + std::vector<size_t> next_dims(dims.begin() + Dims, dims.end()); + std::size_t size = std::accumulate(dims.begin(), + dims.begin() + Dims, + std::size_t{1}, + std::multiplies<size_t>()); + for (size_t i = 0; i < size; ++i) { + inspector<value_type>::prepare(*(val.origin() + i), next_dims); + } + } + + static void assert_c_order(const type& val) { + if (!(val.storage_order() == boost::c_storage_order())) { + throw DataTypeException("Only C storage order is supported for 'boost::multi_array'."); + } + } + + static hdf5_type* data(type& val) { + assert_c_order(val); + return inspector<value_type>::data(*val.data()); + } + + static const hdf5_type* data(const type& val) { + assert_c_order(val); + return inspector<value_type>::data(*val.data()); + } + + template <class It> + static void serialize(const type& val, const std::vector<size_t>& dims, It m) { + assert_c_order(val); + size_t size = val.num_elements(); + auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(subdims); + for (size_t i = 0; i < size; ++i) { + inspector<value_type>::serialize(*(val.origin() + i), subdims, m + i * subsize); + } + } + + template <class It> + static void unserialize(It vec_align, const std::vector<size_t>& dims, type& val) { + assert_c_order(val); + std::vector<size_t> next_dims(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(next_dims); + for (size_t i = 0; i < val.num_elements(); ++i) { + inspector<value_type>::unserialize(vec_align + i * subsize, + next_dims, + *(val.origin() + i)); + } + } +}; + +} // namespace details +} // namespace HighFive diff --git a/packages/HighFive/include/highfive/boost_span.hpp b/packages/HighFive/include/highfive/boost_span.hpp new file mode 100644 index 0000000000000000000000000000000000000000..953479e97b92448d083d8a00993a36f11fe67edf --- /dev/null +++ b/packages/HighFive/include/highfive/boost_span.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "bits/H5Inspector_decl.hpp" +#include "H5Exception.hpp" +#include "bits/inspector_stl_span_misc.hpp" + +#include <boost/core/span.hpp> + +namespace HighFive { +namespace details { +template <class T, std::size_t Extent> +struct inspector<boost::span<T, Extent>>: public inspector_stl_span<boost::span<T, Extent>> { + private: + using super = inspector_stl_span<boost::span<T, Extent>>; + + public: + using type = typename super::type; + using value_type = typename super::value_type; + using base_type = typename super::base_type; + using hdf5_type = typename super::hdf5_type; +}; + +} // namespace details +} // namespace HighFive diff --git a/packages/HighFive/include/highfive/boost_ublas.hpp b/packages/HighFive/include/highfive/boost_ublas.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e3b5b717dea6003d7cb2678b3212def2f559d6f2 --- /dev/null +++ b/packages/HighFive/include/highfive/boost_ublas.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "bits/H5Inspector_decl.hpp" +#include "H5Exception.hpp" + +#include <boost/numeric/ublas/matrix.hpp> + +namespace HighFive { +namespace details { + +template <typename T> +struct inspector<boost::numeric::ublas::matrix<T>> { + using type = boost::numeric::ublas::matrix<T>; + using value_type = unqualified_t<T>; + using base_type = typename inspector<value_type>::base_type; + using hdf5_type = typename inspector<value_type>::hdf5_type; + + static constexpr size_t ndim = 2; + static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim; + static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim; + + static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value && + inspector<value_type>::is_trivially_copyable; + static constexpr bool is_trivially_nestable = false; + + static size_t getRank(const type& val) { + return ndim + inspector<value_type>::getRank(val(0, 0)); + } + + static std::vector<size_t> getDimensions(const type& val) { + std::vector<size_t> sizes{val.size1(), val.size2()}; + auto s = inspector<value_type>::getDimensions(val(0, 0)); + sizes.insert(sizes.end(), s.begin(), s.end()); + return sizes; + } + + static void prepare(type& val, const std::vector<size_t>& dims) { + if (dims.size() < ndim) { + std::ostringstream os; + os << "Impossible to pair DataSet with " << dims.size() << " dimensions into a " << ndim + << " boost::numeric::ublas::matrix"; + throw DataSpaceException(os.str()); + } + val.resize(dims[0], dims[1], false); + } + + static hdf5_type* data(type& val) { + return inspector<value_type>::data(val(0, 0)); + } + + static const hdf5_type* data(const type& val) { + return inspector<value_type>::data(val(0, 0)); + } + + static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* m) { + size_t size = val.size1() * val.size2(); + auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(subdims); + for (size_t i = 0; i < size; ++i) { + inspector<value_type>::serialize(*(&val(0, 0) + i), subdims, m + i * subsize); + } + } + + static void unserialize(const hdf5_type* vec_align, + const std::vector<size_t>& dims, + type& val) { + std::vector<size_t> next_dims(dims.begin() + ndim, dims.end()); + size_t subsize = compute_total_size(next_dims); + size_t size = val.size1() * val.size2(); + for (size_t i = 0; i < size; ++i) { + inspector<value_type>::unserialize(vec_align + i * subsize, + next_dims, + *(&val(0, 0) + i)); + } + } +}; + +} // namespace details +} // namespace HighFive diff --git a/packages/HighFive/include/highfive/span.hpp b/packages/HighFive/include/highfive/span.hpp index ab53319ee657f59cdec2605f07eba9d23979bc44..eb0dd1571b3a6f614b76daae20559e4006b052bb 100644 --- a/packages/HighFive/include/highfive/span.hpp +++ b/packages/HighFive/include/highfive/span.hpp @@ -10,7 +10,7 @@ #pragma once #include "bits/H5Inspector_decl.hpp" -#include "H5Exception.hpp" +#include "bits/inspector_stl_span_misc.hpp" #include <span> @@ -18,84 +18,15 @@ namespace HighFive { namespace details { template <class T, std::size_t Extent> -struct inspector<std::span<T, Extent>> { - using type = std::span<T, Extent>; - using value_type = unqualified_t<T>; - using base_type = typename inspector<value_type>::base_type; - using hdf5_type = typename inspector<value_type>::hdf5_type; - - static constexpr size_t ndim = 1; - static constexpr size_t min_ndim = ndim + inspector<value_type>::min_ndim; - static constexpr size_t max_ndim = ndim + inspector<value_type>::max_ndim; - - static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value && - inspector<value_type>::is_trivially_nestable; - static constexpr bool is_trivially_nestable = false; - - - static size_t getRank(const type& val) { - if (!val.empty()) { - return ndim + inspector<value_type>::getRank(val[0]); - } else { - return min_ndim; - } - } - - static std::vector<size_t> getDimensions(const type& val) { - auto rank = getRank(val); - std::vector<size_t> sizes(rank, 1ul); - sizes[0] = val.size(); - if (!val.empty()) { - auto s = inspector<value_type>::getDimensions(val[0]); - assert(s.size() + ndim == sizes.size()); - for (size_t i = 0; i < s.size(); ++i) { - sizes[i + ndim] = s[i]; - } - } - return sizes; - } - - static void prepare(type& val, const std::vector<size_t>& expected_dims) { - auto actual_dims = getDimensions(val); - if (actual_dims.size() != expected_dims.size()) { - throw DataSpaceException("Mismatching rank."); - } - - for (size_t i = 0; i < actual_dims.size(); ++i) { - if (actual_dims[i] != expected_dims[i]) { - throw DataSpaceException("Mismatching dimensions."); - } - } - } - - static hdf5_type* data(type& val) { - return val.empty() ? nullptr : inspector<value_type>::data(val[0]); - } - - static const hdf5_type* data(const type& val) { - return val.empty() ? nullptr : inspector<value_type>::data(val[0]); - } - - template <class It> - static void serialize(const type& val, const std::vector<size_t>& dims, It m) { - if (!val.empty()) { - auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(subdims); - for (const auto& e: val) { - inspector<value_type>::serialize(e, subdims, m); - m += subsize; - } - } - } - - template <class It> - static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) { - std::vector<size_t> subdims(dims.begin() + ndim, dims.end()); - size_t subsize = compute_total_size(subdims); - for (size_t i = 0; i < dims[0]; ++i) { - inspector<value_type>::unserialize(vec_align + i * subsize, subdims, val[i]); - } - } +struct inspector<std::span<T, Extent>>: public inspector_stl_span<std::span<T, Extent>> { + private: + using super = inspector_stl_span<std::span<T, Extent>>; + + public: + using type = typename super::type; + using value_type = typename super::value_type; + using base_type = typename super::base_type; + using hdf5_type = typename super::hdf5_type; }; } // namespace details diff --git a/packages/HighFive/src/examples/CMakeLists.txt b/packages/HighFive/src/examples/CMakeLists.txt index fa6f5f739ace8c585765025d2f14a4179a6ff83b..8b278df0a673d737efd15d8bda0c5a73b49aa9aa 100644 --- a/packages/HighFive/src/examples/CMakeLists.txt +++ b/packages/HighFive/src/examples/CMakeLists.txt @@ -59,36 +59,36 @@ function(compile_example example_source) string(REPLACE ".cpp" "_bin" example_name ${example_filename}) add_executable(${example_name} ${example_source}) - target_link_libraries(${example_name} PUBLIC HighFive HighFiveWarnings HighFiveFlags) - if(${ARGC} EQUAL 2) - target_link_libraries(${example_name} PUBLIC ${ARGV1}) + target_link_libraries(${example_name} PUBLIC HighFive HighFiveWarnings) + if(${ARGC} GREATER_EQUAL 2) + target_link_libraries(${example_name} PUBLIC ${ARGV1} ${ARGV2} ${ARGV3} ${ARGV4}) endif() endfunction() foreach(example_source ${core_examples}) - compile_example(${example_source}) + compile_example(${example_source} HighFiveFlags) endforeach() foreach(example_source ${easy_examples}) - compile_example(${example_source} HighFiveOptionalDependencies) + compile_example(${example_source} HighFiveFlags HighFiveOptionalDependencies) endforeach() if(HIGHFIVE_TEST_SPAN) foreach(example_source ${span_examples}) - compile_example(${example_source}) + compile_example(${example_source} HighFiveFlags) endforeach() endif() if(HIGHFIVE_TEST_BOOST) foreach(example_source ${boost_examples}) - compile_example(${example_source} HighFiveBoostDependency) + compile_example(${example_source} HighFiveFlags HighFiveBoostDependency) endforeach() endif() if(HIGHFIVE_TEST_EIGEN) foreach(example_source ${eigen_examples}) - compile_example(${example_source} HighFiveEigenDependency) + compile_example(${example_source} HighFiveFlags HighFiveEigenDependency) endforeach() endif() @@ -105,7 +105,7 @@ if(${HDF5_HL_FOUND}) target_link_libraries(HighFiveHlHdf5Dependency ${HDF5_HL_LIBRARIES}) foreach(example_source ${hl_hdf5_examples}) - compile_examples(${example_source} HighFiveHlHdf5Dependency) + compile_examples(${example_source} HighFiveFlags HighFiveHlHdf5Dependency) endforeach() endif() diff --git a/packages/HighFive/src/examples/parallel_hdf5_collective_io.cpp b/packages/HighFive/src/examples/parallel_hdf5_collective_io.cpp index 7261b7cf1d8b8a92623ef166bea72156250d9b7b..1189702b0915a74e741111c3ec53f096a8bc618b 100644 --- a/packages/HighFive/src/examples/parallel_hdf5_collective_io.cpp +++ b/packages/HighFive/src/examples/parallel_hdf5_collective_io.cpp @@ -92,7 +92,7 @@ int main(int argc, char** argv) { std::vector<size_t> offset{std::size_t(mpi_rank), 0ul}; std::vector<size_t> count{1ul, 2ul}; - dataset.select(offset, count).write(data, xfer_props); + dataset.select(offset, count).squeezeMemSpace({0}).write(data, xfer_props); check_collective_io(xfer_props); // Let's ensure that everything has been written do disk. @@ -104,7 +104,7 @@ int main(int argc, char** argv) { // MPI ranks don't have to read non-overlapping parts, but in this // example they happen to. Again all rank participate in this call. - dataset.select(offset, count).read(data, xfer_props); + dataset.select(offset, count).squeezeMemSpace({0}).read(data, xfer_props); check_collective_io(xfer_props); } catch (Exception& err) { diff --git a/packages/HighFive/tests/cmake_integration/test_cmake_integration.sh b/packages/HighFive/tests/cmake_integration/test_cmake_integration.sh index cf80fbfdda5d518da026abaeb77a7fd2b2b07f12..74fd743c1fa068aa09986953abb12588181c5ccc 100644 --- a/packages/HighFive/tests/cmake_integration/test_cmake_integration.sh +++ b/packages/HighFive/tests/cmake_integration/test_cmake_integration.sh @@ -16,6 +16,15 @@ HIGHFIVE_INSTALL_DIR="${HIGHFIVE_BUILD_DIR}/install" export HIGHFIVE_GIT_REPOSITORY="file://$(realpath "$HIGHFIVE_DIR")" export HIGHFIVE_GIT_TAG=$(git rev-parse HEAD) +make_submodule() { + local project_dir="$1" + local dep_dir="${project_dir}/deps/HighFive" + + rm "${dep_dir}" || true + mkdir -p "$(dirname "${dep_dir}")" + ln -sf "${HIGHFIVE_DIR}" "${dep_dir}" +} + test_dependent_library() { local project="dependent_library" local project_dir="${TEST_DIR}/${project}" @@ -35,33 +44,35 @@ test_dependent_library() { cmake --build "${build_dir}" --verbose --target install - local test_project="test_dependent_library" - local test_build_dir="${TMP_DIR}/test_build" - local test_install_dir="${TMP_DIR}/test_build/install" - rm -rf ${test_build_dir} || true + for vendor in submodule fetch_content external none + do + local test_project="test_dependent_library" + local test_build_dir="${TMP_DIR}/test_build" + local test_install_dir="${TMP_DIR}/test_build/install" + + make_submodule ${test_project} - cmake -DUSE_BOOST=${use_boost} \ - -DCMAKE_PREFIX_PATH="${HIGHFIVE_INSTALL_DIR};${install_dir}" \ - -DCMAKE_INSTALL_PREFIX="${test_install_dir}" \ - -B "${test_build_dir}" "${test_project}" - cmake --build "${test_build_dir}" --verbose - ctest --test-dir "${test_build_dir}" --verbose + rm -rf ${test_build_dir} || true + cmake -DUSE_BOOST=${use_boost} \ + -DVENDOR_STRATEGY=${vendor} \ + -DCMAKE_PREFIX_PATH="${HIGHFIVE_INSTALL_DIR};${install_dir}" \ + -DCMAKE_INSTALL_PREFIX="${test_install_dir}" \ + -B "${test_build_dir}" "${test_project}" + + cmake --build "${test_build_dir}" --verbose + ctest --test-dir "${test_build_dir}" --verbose + done done } test_application() { local project="application" local project_dir="${TEST_DIR}/${project}" - local dep_dir="${TEST_DIR}/${project}/deps/HighFive" - - rm "${dep_dir}" || true - ln -sf "${HIGHFIVE_DIR}" "${dep_dir}" - echo ${HIGHFIVE_DIR} - echo ${dep_dir} + make_submodule ${project_dir} for vendor in submodule fetch_content external do diff --git a/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeList.txt b/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeList.txt deleted file mode 100644 index 08bc4a4adfce66472faa808e8ae8e9b13204ea55..0000000000000000000000000000000000000000 --- a/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeList.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(TestHi5Dependent VERSION 0.1) - -if(NOT DEFINED CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 14) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_CXX_EXTENSIONS OFF) -endif() - -add_executable(test_hi5_dependent test_dependent_library.cpp) - -if(NOT USE_BOOST) - find_package(Hi5Dependent REQUIRED) -else() - find_package(Hi5Dependent REQUIRED COMPONENTS boost) - target_link_libraries(test_hi5_dependent PUBLIC Hi5Dependent::Boost) -endif() - -target_link_libraries(test_hi5_dependent PUBLIC Hi5Dependent::Read Hi5Dependent::Write) - -enable_testing() -add_test(NAME test_hi5_dependent COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_hi5_dependent) diff --git a/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeLists.txt b/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeLists.txt index c9023f98ac85600f0f6f8cc0b952373b72bf72e5..15851c3c67265c41e2290b6b0bc04d393d0920d5 100644 --- a/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeLists.txt +++ b/packages/HighFive/tests/cmake_integration/test_dependent_library/CMakeLists.txt @@ -7,8 +7,31 @@ if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_EXTENSIONS OFF) endif() +set(VENDOR_STRATEGY "submodule" CACHE STRING "Use 'submodule' for Git submodules, 'fetch_content' for FetchContent, 'external' for `find_package`, 'none' for making to attempt at finding HighFive.") + add_executable(test_hi5_dependent test_dependent_library.cpp) +if(${VENDOR_STRATEGY} STREQUAL "submodule") + # When vendoring via a Git submodule, this is the correct + # line to include HighFive. + add_subdirectory("deps/HighFive" EXCLUDE_FROM_ALL) +elseif(${VENDOR_STRATEGY} STREQUAL "fetch_content") + include(FetchContent) + FetchContent_Declare(HighFive + GIT_REPOSITORY $ENV{HIGHFIVE_GIT_REPOSITORY} + GIT_TAG $ENV{HIGHFIVE_GIT_TAG} + ) + FetchContent_MakeAvailable(HighFive) +elseif(${VENDOR_STRATEGY} STREQUAL "external") + # When HighFive is installed like regular software and then "found", do the + # following: + find_package(HighFive REQUIRED) +endif() + +if(NOT ${VENDOR_STRATEGY} STREQUAL "none") + target_link_libraries(test_hi5_dependent PUBLIC HighFive::HighFive) +endif() + if(NOT USE_BOOST) find_package(Hi5Dependent REQUIRED) target_link_libraries(test_hi5_dependent PUBLIC Hi5Dependent::Read Hi5Dependent::Write) diff --git a/packages/HighFive/tests/unit/CMakeLists.txt b/packages/HighFive/tests/unit/CMakeLists.txt index 10cff1f6fe24a5c8c3e4927bc7ec083fd85fdcc5..fc682e9350db6f1c9b600f766c7e95f0a65de953 100644 --- a/packages/HighFive/tests/unit/CMakeLists.txt +++ b/packages/HighFive/tests/unit/CMakeLists.txt @@ -19,7 +19,7 @@ if(HDF5_IS_PARALLEL) ## parallel MPI tests add_executable(tests_parallel_bin ${tests_parallel_src}) - target_link_libraries(tests_parallel_bin HighFive HighFiveWarnings HighFiveFlags Catch2::Catch2) + target_link_libraries(tests_parallel_bin HighFive HighFiveWarnings Catch2::Catch2) target_link_libraries(tests_parallel_bin HighFiveOptionalDependencies) # We need to patch in a call to `mpirun` or equivalent when using @@ -51,7 +51,11 @@ foreach(PUBLIC_HEADER ${public_headers}) continue() endif() - if(PUBLIC_HEADER STREQUAL "highfive/boost.hpp" AND NOT HIGHFIVE_TEST_BOOST) + if(PUBLIC_HEADER MATCHES "highfive/boost.*.hpp" AND NOT HIGHFIVE_TEST_BOOST) + continue() + endif() + + if(PUBLIC_HEADER STREQUAL "highfive/boost_span.hpp" AND NOT HIGHFIVE_TEST_BOOST_SPAN) continue() endif() diff --git a/packages/HighFive/tests/unit/data_generator.hpp b/packages/HighFive/tests/unit/data_generator.hpp index e4e7dfa85a28f50bdaa53fdd59c5bc9afa5cc4c9..6da177b24d4e7df10b83b842c928104d465e80a4 100644 --- a/packages/HighFive/tests/unit/data_generator.hpp +++ b/packages/HighFive/tests/unit/data_generator.hpp @@ -13,6 +13,10 @@ #include <highfive/boost.hpp> #endif +#ifdef HIGHFIVE_TEST_BOOST_SPAN +#include <highfive/boost_span.hpp> +#endif + #ifdef HIGHFIVE_TEST_EIGEN #include <highfive/eigen.hpp> #endif @@ -68,7 +72,7 @@ std::vector<size_t> unravel(size_t flat_index, const Dims& dims) { } template <class Dims> -static size_t flat_size(const Dims& dims) { +size_t flat_size(const Dims& dims) { size_t n = 1; for (auto d: dims) { n *= d; @@ -236,12 +240,15 @@ struct ContainerTraits<std::array<T, N>>: public STLLikeContainerTraits<std::arr } }; - -#ifdef HIGHFIVE_TEST_SPAN -template <class T, std::size_t Extent> -struct ContainerTraits<std::span<T, Extent>>: public STLLikeContainerTraits<std::span<T, Extent>> { +// Anything with the same API as `std::span` can implemented by inheriting from +// this class. +// +// The template parameter `DynamicExtent` is the equivalent of the magic number +// `std::dynamic_extent`. +template <class Span, size_t DynamicExtent> +struct STLSpanLikeContainerTraits: public STLLikeContainerTraits<Span> { private: - using super = STLLikeContainerTraits<std::span<T, Extent>>; + using super = STLLikeContainerTraits<Span>; public: using container_type = typename super::container_type; @@ -274,12 +281,26 @@ struct ContainerTraits<std::span<T, Extent>>: public STLLikeContainerTraits<std: } static void sanitize_dims(std::vector<size_t>& dims, size_t axis) { - if (Extent != std::dynamic_extent) { - dims[axis] = Extent; + if (Span::extent != DynamicExtent) { + dims[axis] = Span::extent; ContainerTraits<value_type>::sanitize_dims(dims, axis + 1); } } }; + + +#ifdef HIGHFIVE_TEST_SPAN +template <class T, std::size_t Extent> +struct ContainerTraits<std::span<T, Extent>> + : public STLSpanLikeContainerTraits<std::span<T, Extent>, std::dynamic_extent> { + private: + using super = STLSpanLikeContainerTraits<std::span<T, Extent>, std::dynamic_extent>; + + public: + using container_type = typename super::container_type; + using value_type = typename super::value_type; + using base_type = typename super::base_type; +}; #endif @@ -428,6 +449,20 @@ struct ContainerTraits<boost::numeric::ublas::matrix<T>> { #endif +#if HIGHFIVE_TEST_BOOST_SPAN +template <class T, std::size_t Extent> +struct ContainerTraits<boost::span<T, Extent>> + : public STLSpanLikeContainerTraits<boost::span<T, Extent>, boost::dynamic_extent> { + private: + using super = STLSpanLikeContainerTraits<boost::span<T, Extent>, boost::dynamic_extent>; + + public: + using container_type = typename super::container_type; + using value_type = typename super::value_type; + using base_type = typename super::base_type; +}; +#endif + // -- Eigen ------------------------------------------------------------------- #if HIGHFIVE_TEST_EIGEN diff --git a/packages/HighFive/tests/unit/supported_types.hpp b/packages/HighFive/tests/unit/supported_types.hpp index 5f1bf6453da895e041e7167efa4c71ea09ccf827..ccda4a77a8e3c3e5296066b908892a4a71a6c916 100644 --- a/packages/HighFive/tests/unit/supported_types.hpp +++ b/packages/HighFive/tests/unit/supported_types.hpp @@ -9,6 +9,10 @@ #include <boost/multi_array.hpp> #endif +#ifdef HIGHFIVE_TEST_BOOST_SPAN +#include <boost/core/span.hpp> +#endif + #ifdef HIGHFIVE_TEST_EIGEN #include <Eigen/Core> #include <Eigen/Dense> @@ -56,6 +60,14 @@ struct BoostUblasMatrix { }; #endif +#ifdef HIGHFIVE_TEST_BOOST_SPAN +template <class C = type_identity> +struct BoostSpan { + template <class T> + using type = boost::span<typename C::template type<T>>; +}; +#endif + #ifdef HIGHFIVE_TEST_EIGEN template <int n, int m, int Option, class C = type_identity> struct EigenMatrix { @@ -153,6 +165,11 @@ using supported_array_types = typename ConcatenateTuples< typename ContainerProduct<STDVector<BoostUblasMatrix<>>, scalar_types_boost>::type, typename ContainerProduct<STDArray<5, BoostUblasMatrix<>>, scalar_types_boost>::type, #endif +#ifdef HIGHFIVE_TEST_BOOST_SPAN + typename ContainerProduct<BoostSpan<>, scalar_types_boost>::type, + typename ContainerProduct<STDVector<BoostSpan<>>, scalar_types_boost>::type, + typename ContainerProduct<STDArray<5, BoostSpan<>>, scalar_types_boost>::type, +#endif #ifdef HIGHFIVE_TEST_EIGEN typename ContainerProduct<EigenMatrix<3, 5, Eigen::ColMajor>, scalar_types_eigen>::type, typename ContainerProduct<EigenMatrix<3, 5, Eigen::RowMajor>, scalar_types_eigen>::type, diff --git a/packages/HighFive/tests/unit/test_all_types.cpp b/packages/HighFive/tests/unit/test_all_types.cpp index 0a18e0257a8113195436d056dcdc7c0fa583dc5b..4d3ed0a0abe1fc58415adf7a22dd04c42776722c 100644 --- a/packages/HighFive/tests/unit/test_all_types.cpp +++ b/packages/HighFive/tests/unit/test_all_types.cpp @@ -112,7 +112,8 @@ void check_read_regular(const std::string& file_name, const std::vector<size_t>& using reference_type = typename testing::MultiDimVector<base_type, traits::rank>::type; auto file = File(file_name, File::Truncate); - auto expected = testing::copy<reference_type>(traits::create(dims), dims); + auto raw_expected = traits::create(dims); + auto expected = testing::copy<reference_type>(raw_expected, dims); auto dataspace = DataSpace(dims); auto attr = testing::AttributeCreateTraits::create<base_type>(file, "dset", dataspace); @@ -139,6 +140,7 @@ void check_read_regular(const std::string& file_name, const std::vector<size_t>& } testing::ContainerTraits<reference_type>::deallocate(expected, dims); + testing::ContainerTraits<Container>::deallocate(raw_expected, dims); } template <class Container> diff --git a/packages/HighFive/tests/unit/test_high_five_selection.cpp b/packages/HighFive/tests/unit/test_high_five_selection.cpp index e3b91e4cc1b9793e7d69e2beb396865c95f6a0e2..eb27bd6a9abff305147db1205ffa044896c89559 100644 --- a/packages/HighFive/tests/unit/test_high_five_selection.cpp +++ b/packages/HighFive/tests/unit/test_high_five_selection.cpp @@ -25,6 +25,7 @@ #include <highfive/highfive.hpp> #include "tests_high_five.hpp" +#include "data_generator.hpp" using namespace HighFive; using Catch::Matchers::Equals; @@ -415,33 +416,33 @@ std::vector<IrregularHyperSlabTestData> make_irregular_hyperslab_test_data() { // xor, irregular auto slab_ab_xor = HyperSlab(slabs["a"]) ^ slabs["b"]; // clang-format off - auto answer_ab_xor = IrregularHyperSlabAnswer{{ - {1ul, 1ul}, {1ul, 2ul}, - {2ul, 0ul}, {2ul, 2ul}, - {3ul, 1ul}, {3ul, 2ul} - }}; + auto answer_ab_xor = IrregularHyperSlabAnswer{{ + {1ul, 1ul}, {1ul, 2ul}, + {2ul, 0ul}, {2ul, 2ul}, + {3ul, 1ul}, {3ul, 2ul} + }}; // clang-format on test_data.push_back({"a xor b", slab_ab_xor, answer_ab_xor}); // (not a) and e, irregular auto slab_ab_nota = HyperSlab(slabs["a"]).notA(slabs["b"]); // clang-format off - auto answer_ab_nota = IrregularHyperSlabAnswer{{ - {1ul, 1ul}, {1ul, 2ul}, - {2ul, 2ul}, - {3ul, 1ul}, {3ul, 2ul} - }}; + auto answer_ab_nota = IrregularHyperSlabAnswer{{ + {1ul, 1ul}, {1ul, 2ul}, + {2ul, 2ul}, + {3ul, 1ul}, {3ul, 2ul} + }}; // clang-format on test_data.push_back({"a nota b", slab_ab_nota, answer_ab_nota}); // (not a) and e, irregular auto slab_ba_notb = HyperSlab(slabs["b"]).notB(slabs["a"]); // clang-format off - auto answer_ba_notb = IrregularHyperSlabAnswer{{ - {1ul, 1ul}, {1ul, 2ul}, - {2ul, 2ul}, - {3ul, 1ul}, {3ul, 2ul} - }}; + auto answer_ba_notb = IrregularHyperSlabAnswer{{ + {1ul, 1ul}, {1ul, 2ul}, + {2ul, 2ul}, + {3ul, 1ul}, {3ul, 2ul} + }}; // clang-format on test_data.push_back({"b notb a", slab_ba_notb, answer_ba_notb}); @@ -534,3 +535,139 @@ void irregularHyperSlabSelectionWriteTest() { TEMPLATE_LIST_TEST_CASE("irregularHyperSlabSelectionWrite", "[template]", std::tuple<int>) { irregularHyperSlabSelectionWriteTest<TestType>(); } + +void check_selected(const std::vector<int>& selected, + const std::vector<std::array<size_t, 2>>& indices, + const std::vector<std::vector<int>>& x) { + REQUIRE(selected.size() == indices.size()); + for (size_t k = 0; k < selected.size(); ++k) { + size_t i = indices[k][0]; + size_t j = indices[k][1]; + REQUIRE(selected[k] == x[i][j]); + } +} + +TEST_CASE("select_multiple_ors", "[hyperslab]") { + size_t n = 100, m = 20; + size_t nsel = 30; + auto x = testing::DataGenerator<std::vector<std::vector<int>>>::create({n, m}); + + auto file = File("select_multiple_ors.h5", File::Truncate); + auto dset = file.createDataSet("x", x); + + std::vector<std::array<size_t, 2>> indices; + auto hyperslab = HyperSlab(); + for (size_t i = 0; i < nsel; ++i) { + std::vector<size_t> offsets{i, i % 10}; + std::vector<size_t> counts{1, 3}; + hyperslab |= RegularHyperSlab(offsets, counts); + + for (size_t k = 0; k < counts[1]; ++k) { + indices.push_back({offsets[0], offsets[1] + k}); + } + } + + SECTION("Pure Or Chain") { + auto selected = dset.select(hyperslab).read<std::vector<int>>(); + check_selected(selected, indices, x); + } + + SECTION("Or Chain And Slab") { + std::vector<size_t> offsets{5, 2}; + std::vector<size_t> counts{85, 12}; + + std::vector<std::array<size_t, 2>> selected_indices; + for (const auto ij: indices) { + std::array<size_t, 2> ij_max = {offsets[0] + counts[0], offsets[1] + counts[1]}; + + if (offsets[0] <= ij[0] && ij[0] < ij_max[0] && offsets[1] <= ij[1] && + ij[1] < ij_max[1]) { + selected_indices.push_back(ij); + } + } + + hyperslab &= RegularHyperSlab(offsets, counts); + + auto selected = dset.select(hyperslab).read<std::vector<int>>(); + check_selected(selected, selected_indices, x); + } +} + +TEST_CASE("select_multiple_ors_edge_cases", "[hyperslab]") { + size_t n = 100, m = 20; + + auto x = testing::DataGenerator<std::vector<std::vector<int>>>::create({n, m}); + auto file = File("select_multiple_ors_edge_cases.h5", File::Truncate); + auto dset = file.createDataSet("x", x); + + std::vector<std::array<size_t, 2>> indices; + for (size_t i = 0; i < n; ++i) { + for (size_t j = 0; j < m; ++j) { + indices.push_back({i, j}); + } + } + + auto space = DataSpace({n, m}); + SECTION("complete |= ") { + auto hyperslab = HyperSlab(RegularHyperSlab({0, 0}, {n, m})); + + // Select everything; and then redundantly some parts again. + hyperslab &= RegularHyperSlab({0, 0}, {n, m}); + hyperslab |= RegularHyperSlab({0, 0}, {n, m / 2}); + hyperslab |= RegularHyperSlab({3, 0}, {1, 3}); + hyperslab |= RegularHyperSlab({6, 0}, {1, 3}); + hyperslab.apply(space); + + auto selected = dset.select(hyperslab).read<std::vector<int>>(); + check_selected(selected, indices, x); + } + + SECTION("complete &=") { + auto hyperslab = HyperSlab(); + + // With detours, select everything, then reduce it to the first 2 + // elements. + hyperslab |= RegularHyperSlab({0, 0}, {n, m / 2}); + hyperslab |= RegularHyperSlab({0, 0}, {n, m / 2}); + hyperslab |= RegularHyperSlab({0, m / 2}, {n, m - m / 2}); + hyperslab |= RegularHyperSlab({0, 0}, {n, m}); + hyperslab &= RegularHyperSlab({0, 0}, {1, 2}); + hyperslab.apply(space); + + indices = {{0, 0}, {0, 1}}; + + auto selected = dset.select(hyperslab).read<std::vector<int>>(); + check_selected(selected, indices, x); + } + + SECTION("empty |=") { + auto hyperslab = HyperSlab(RegularHyperSlab({0, 0}, {n, m})); + hyperslab &= RegularHyperSlab({0, 0}, {1, 2}); + hyperslab &= RegularHyperSlab({3, 0}, {1, 2}); + + hyperslab |= RegularHyperSlab({0, 0}, {n, m / 2}); + hyperslab |= RegularHyperSlab({0, 0}, {n, m / 2}); + hyperslab |= RegularHyperSlab({0, m / 2}, {n, m - m / 2}); + hyperslab |= RegularHyperSlab({0, 0}, {n, m}); + + hyperslab.apply(space); + + auto selected = dset.select(hyperslab).read<std::vector<int>>(); + check_selected(selected, indices, x); + } + + SECTION("|= empty") { + auto hyperslab = HyperSlab(); + + hyperslab |= RegularHyperSlab({0, 0}, {1, 2}); + hyperslab |= RegularHyperSlab({0, 0}, {1, 2}); + hyperslab |= RegularHyperSlab({0, 0}, {0, 0}); + + hyperslab.apply(space); + + indices = {{0, 0}, {0, 1}}; + + auto selected = dset.select(hyperslab).read<std::vector<int>>(); + check_selected(selected, indices, x); + } +} diff --git a/packages/HighFive/tests/unit/test_string.cpp b/packages/HighFive/tests/unit/test_string.cpp index ed6a4a5bfac67cb90d85438669ae449dde171711..72065455c109ad91ba60042678e781e7482269d9 100644 --- a/packages/HighFive/tests/unit/test_string.cpp +++ b/packages/HighFive/tests/unit/test_string.cpp @@ -206,6 +206,48 @@ void check_multiple_string(File file, size_t string_length) { } } +template <class CreateTraits> +void check_supposedly_nullterm(HighFive::File& file, size_t string_length) { + auto dataspace = HighFive::DataSpace::Scalar(); + auto datatype = HighFive::FixedLengthStringType(string_length, + HighFive::StringPadding::NullTerminated); + auto obj = CreateTraits::create(file, + "not_null_terminated_" + std::to_string(string_length), + dataspace, + datatype); + + // Creates an `string_length` byte, "null-terminated", fixed-length string. The first + // `string_length` bytes are filled with "a"s. Clearly, this isn't null-terminated. However, + // h5py will read it back, HDF5 allows us to create these; and they're + // found in the wild. + std::string value(string_length, 'a'); + obj.write_raw(value.c_str(), datatype); + + auto actual = obj.template read<std::string>(); + REQUIRE(actual == value); +} + +template <class CreateTraits> +void check_supposedly_nullterm_scan(HighFive::File& file) { + for (size_t n = 1; n < 256; ++n) { + check_supposedly_nullterm<CreateTraits>(file, n); + } + + check_supposedly_nullterm<CreateTraits>(file, 4091); + check_supposedly_nullterm<CreateTraits>(file, 4092); + check_supposedly_nullterm<CreateTraits>(file, 4093); +} + +TEST_CASE("HighFiveSTDString (attribute, nullterm cornercase)") { + auto file = HighFive::File("not_null_terminated_attribute.h5", HighFive::File::Truncate); + check_supposedly_nullterm_scan<testing::AttributeCreateTraits>(file); +} + +TEST_CASE("HighFiveSTDString (dataset, nullterm cornercase)") { + auto file = HighFive::File("not_null_terminated_dataset.h5", HighFive::File::Truncate); + check_supposedly_nullterm_scan<testing::DataSetCreateTraits>(file); +} + TEST_CASE("HighFiveSTDString (dataset, single, short)") { File file("std_string_dataset_single_short.h5", File::Truncate); check_single_string<testing::DataSetCreateTraits>(file, 3); diff --git a/packages/HighFive/tests/unit/tests_high_five_base.cpp b/packages/HighFive/tests/unit/tests_high_five_base.cpp index 02ce7a04de746187e096a274b0a6a78f930af450..06c94ecbef6ffc241297399372c8329305d99f3f 100644 --- a/packages/HighFive/tests/unit/tests_high_five_base.cpp +++ b/packages/HighFive/tests/unit/tests_high_five_base.cpp @@ -114,16 +114,22 @@ TEST_CASE("Test open modes in HighFive") { CHECK_THROWS_AS(File(file_name, File::ReadWrite), FileException); // But with Create flag should be fine - { File file(file_name, File::ReadWrite | File::Create); } + { + File file(file_name, File::ReadWrite | File::Create); + } // But if its there and exclusive is given, should fail CHECK_THROWS_AS(File(file_name, File::ReadWrite | File::Excl), FileException); // ReadWrite and Excl flags are fine together (posix) std::remove(file_name.c_str()); - { File file(file_name, File::ReadWrite | File::Excl); } + { + File file(file_name, File::ReadWrite | File::Excl); + } // All three are fine as well (as long as the file does not exist) std::remove(file_name.c_str()); - { File file(file_name, File::ReadWrite | File::Create | File::Excl); } + { + File file(file_name, File::ReadWrite | File::Create | File::Excl); + } // Just a few combinations are incompatible, detected by hdf5lib CHECK_THROWS_AS(File(file_name, File::Truncate | File::Excl), FileException); @@ -132,13 +138,38 @@ TEST_CASE("Test open modes in HighFive") { CHECK_THROWS_AS(File(file_name, File::Truncate | File::Excl), FileException); // But in most cases we will truncate and that should always work - { File file(file_name, File::Truncate); } + { + File file(file_name, File::Truncate); + } std::remove(file_name.c_str()); - { File file(file_name, File::Truncate); } + { + File file(file_name, File::Truncate); + } // Last but not least, defaults should be ok - { File file(file_name); } // ReadOnly - { File file(file_name, 0); } // force empty-flags, does open without flags + { + File file(file_name); + } // ReadOnly +} + +void check_access_mode(File::AccessMode mode) { + CHECK(any(mode)); + CHECK(((File::ReadOnly | mode) & File::ReadOnly) == File::AccessMode::ReadOnly); + CHECK(!any(File::ReadOnly & mode)); + CHECK(((File::ReadOnly | mode) ^ mode) == File::AccessMode::ReadOnly); + CHECK((File::ReadOnly & ~mode) == File::AccessMode::ReadOnly); + CHECK(!any(mode & ~mode)); +} + +TEST_CASE("File::AccessMode") { + CHECK(!any(File::AccessMode::None)); + CHECK(any(File::ReadOnly)); + + check_access_mode(File::ReadWrite); + check_access_mode(File::Truncate); + check_access_mode(File::Excl); + check_access_mode(File::Debug); + check_access_mode(File::Create); } TEST_CASE("Test file version bounds") { @@ -2518,6 +2549,12 @@ TEST_CASE("Version Numbers") { std::to_string(patch); CHECK(version == expected); + +#if defined(HIGHFIVE_VERSION_PRERELEASE) + int prerelease = HIGHFIVE_VERSION_PRERELEASE; + expected += "-beta" + std::to_string(prerelease); +#endif + CHECK(HIGHFIVE_VERSION_STRING == expected); }