From 49151a01912ec36633cba1fe8d023f6f82d6454a Mon Sep 17 00:00:00 2001 From: Antony Chan Date: Fri, 5 Dec 2025 09:07:55 -0800 Subject: [PATCH 1/4] Avoid memory allocation with `H5Sselect_hyperslab` Implement `HighFive::RegularHyperSlabNoMalloc` that takes hyperslab variables in stack space. Do not construct `std::vector`. Similarly, implement `::select(RegularHyperSlabNoMalloc<>)` such that it is free of `operator new[]` calls before invoking `H5Sselect_hyperslab`. Ensure that when hyperslab offset and range are compile-time constants, compiler (with arguments `-O3 -flto`) will inline everything without any `operator new[]` statements. --- include/highfive/H5DataSpace.hpp | 2 +- include/highfive/bits/H5Dataspace_misc.hpp | 9 ++-- include/highfive/bits/H5Slice_traits.hpp | 46 ++++++++++++++++++- include/highfive/bits/H5Slice_traits_misc.hpp | 18 ++++++++ src/examples/select_partial_dataset_cpp11.cpp | 5 ++ 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/include/highfive/H5DataSpace.hpp b/include/highfive/H5DataSpace.hpp index 696a07ab..7b2f444a 100644 --- a/include/highfive/H5DataSpace.hpp +++ b/include/highfive/H5DataSpace.hpp @@ -82,7 +82,7 @@ class DataSpace: public Object { /// \endcode /// \since 2.3 template - explicit DataSpace(const std::array& dims); + constexpr explicit DataSpace(const std::array& dims); /// \brief Create a DataSpace of N-dimensions from an initializer list. /// \param dims Dimensions of the new DataSpace diff --git a/include/highfive/bits/H5Dataspace_misc.hpp b/include/highfive/bits/H5Dataspace_misc.hpp index 6b55e45b..b20a4914 100644 --- a/include/highfive/bits/H5Dataspace_misc.hpp +++ b/include/highfive/bits/H5Dataspace_misc.hpp @@ -31,15 +31,18 @@ inline DataSpace::DataSpace(const std::vector& dims) : DataSpace(dims.begin(), dims.end()) {} template -inline DataSpace::DataSpace(const std::array& dims) - : DataSpace(dims.begin(), dims.end()) {} +constexpr DataSpace::DataSpace(const std::array& dims) { + std::array real_dims; + std::copy(dims.begin(), dims.end(), real_dims.begin()); + _hid = detail::h5s_create_simple(static_cast(N), real_dims.data(), nullptr); +} inline DataSpace::DataSpace(const std::initializer_list& items) : DataSpace(std::vector(items)) {} template inline DataSpace::DataSpace(size_t dim1, Args... dims) - : DataSpace(std::vector{dim1, static_cast(dims)...}) {} + : DataSpace(std::array{dim1, static_cast(dims)...}) {} template inline DataSpace::DataSpace(const IT begin, const IT end) { diff --git a/include/highfive/bits/H5Slice_traits.hpp b/include/highfive/bits/H5Slice_traits.hpp index 5dd60e78..3360a9f8 100644 --- a/include/highfive/bits/H5Slice_traits.hpp +++ b/include/highfive/bits/H5Slice_traits.hpp @@ -10,6 +10,9 @@ #include #include +#if __cplusplus >= 201703L +#include +#endif #include "H5_definitions.hpp" #include "H5Utils.hpp" @@ -108,6 +111,45 @@ struct RegularHyperSlab { std::vector block; }; +#if __cplusplus >= 201703L +template +struct RegularHyperSlabNoMalloc { + constexpr size_t rank() const { + return Rank; + } + + /// Dimensions when all gaps are removed. + constexpr std::array packedDims() const { + auto dims = std::array{}; + + for (size_t i = 0; i < Rank; ++i) { + dims[i] = count[i] * (block ? (*block)[i] : 1); + } + + return dims; + } + + DataSpace apply(const DataSpace& space_) const { + auto space = space_.clone(); + const auto error_code = H5Sselect_hyperslab(space.getId(), + H5S_SELECT_SET, + offset.data(), + stride ? stride->data() : nullptr, + count.data(), + block ? block->data() : nullptr); + + if (error_code < 0) { + HDF5ErrMapper::ToException("Unable to select hyperslab"); + } + return space; + } + std::array offset{}; + std::array count{}; + std::optional> stride{std::nullopt}; + std::optional> block{std::nullopt}; +}; +#endif + class HyperSlab { public: HyperSlab() { @@ -416,7 +458,6 @@ class ProductSet { friend class SliceTraits; }; - template class SliceTraits { public: @@ -430,6 +471,9 @@ class SliceTraits { /// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays. Selection select(const HyperSlab& hyper_slab) const; + template + Selection select(const RegularHyperSlabNoMalloc& hyper_slab) const; + /// /// \brief Select an \p hyper_slab in the current Slice/Dataset. /// diff --git a/include/highfive/bits/H5Slice_traits_misc.hpp b/include/highfive/bits/H5Slice_traits_misc.hpp index f451ac01..4c082938 100644 --- a/include/highfive/bits/H5Slice_traits_misc.hpp +++ b/include/highfive/bits/H5Slice_traits_misc.hpp @@ -240,6 +240,24 @@ inline Selection SliceTraits::select(const HyperSlab& hyper_slab) cons return detail::make_selection(memspace, filespace, details::get_dataset(slice)); } +template +template +inline Selection SliceTraits::select( + const RegularHyperSlabNoMalloc& hyper_slab) const { + const auto& slice = static_cast(*this); + auto filespace = slice.getSpace(); + filespace = hyper_slab.apply(filespace); + + const auto packed_dims = hyper_slab.packedDims(); + const auto n_elements = + std::accumulate(packed_dims.begin(), + packed_dims.end(), + size_t{1}, + [](const size_t& a, const hsize_t& b) { return a * b; }); + auto memspace = DataSpace(std::array{n_elements}); + + return detail::make_selection(memspace, filespace, details::get_dataset(slice)); +} template inline Selection SliceTraits::select(const std::vector& offset, diff --git a/src/examples/select_partial_dataset_cpp11.cpp b/src/examples/select_partial_dataset_cpp11.cpp index 1e480c16..65a2aa7d 100644 --- a/src/examples/select_partial_dataset_cpp11.cpp +++ b/src/examples/select_partial_dataset_cpp11.cpp @@ -34,6 +34,11 @@ int main(void) { dataset.write(values); // now we read back 2x2 values after an offset of 0x2 + { + std::vector result; + dataset.select(RegularHyperSlabNoMalloc<2>{{0, 2}, {2, 2}}).read(result); + } + std::vector> result; dataset.select({0, 2}, {2, 2}).read(result); From dd8848dfec59931047c5d33ef03fb2f6a5ee2937 Mon Sep 17 00:00:00 2001 From: Antony Chan Date: Thu, 11 Dec 2025 09:25:12 -0800 Subject: [PATCH 2/4] Move RegularHyperSlabNoMalloc out of include/ --- include/highfive/bits/H5Slice_traits.hpp | 45 +-------- include/highfive/bits/H5Slice_traits_misc.hpp | 26 +---- src/examples/select_partial_dataset_cpp11.cpp | 5 - src/examples/select_partial_dataset_cpp17.cpp | 97 +++++++++++++++++++ 4 files changed, 103 insertions(+), 70 deletions(-) create mode 100644 src/examples/select_partial_dataset_cpp17.cpp diff --git a/include/highfive/bits/H5Slice_traits.hpp b/include/highfive/bits/H5Slice_traits.hpp index 3360a9f8..58f9ea6d 100644 --- a/include/highfive/bits/H5Slice_traits.hpp +++ b/include/highfive/bits/H5Slice_traits.hpp @@ -111,45 +111,6 @@ struct RegularHyperSlab { std::vector block; }; -#if __cplusplus >= 201703L -template -struct RegularHyperSlabNoMalloc { - constexpr size_t rank() const { - return Rank; - } - - /// Dimensions when all gaps are removed. - constexpr std::array packedDims() const { - auto dims = std::array{}; - - for (size_t i = 0; i < Rank; ++i) { - dims[i] = count[i] * (block ? (*block)[i] : 1); - } - - return dims; - } - - DataSpace apply(const DataSpace& space_) const { - auto space = space_.clone(); - const auto error_code = H5Sselect_hyperslab(space.getId(), - H5S_SELECT_SET, - offset.data(), - stride ? stride->data() : nullptr, - count.data(), - block ? block->data() : nullptr); - - if (error_code < 0) { - HDF5ErrMapper::ToException("Unable to select hyperslab"); - } - return space; - } - std::array offset{}; - std::array count{}; - std::optional> stride{std::nullopt}; - std::optional> block{std::nullopt}; -}; -#endif - class HyperSlab { public: HyperSlab() { @@ -469,10 +430,8 @@ class SliceTraits { /// nicely into a multi-dimensional array, but only a subset of such an array. /// /// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays. - Selection select(const HyperSlab& hyper_slab) const; - - template - Selection select(const RegularHyperSlabNoMalloc& hyper_slab) const; + template + Selection select(const HyperSlabInterface& hyper_slab) const; /// /// \brief Select an \p hyper_slab in the current Slice/Dataset. diff --git a/include/highfive/bits/H5Slice_traits_misc.hpp b/include/highfive/bits/H5Slice_traits_misc.hpp index 4c082938..87943dd9 100644 --- a/include/highfive/bits/H5Slice_traits_misc.hpp +++ b/include/highfive/bits/H5Slice_traits_misc.hpp @@ -229,32 +229,14 @@ inline Selection SliceTraits::select(const HyperSlab& hyper_slab, } template -inline Selection SliceTraits::select(const HyperSlab& hyper_slab) const { +template +inline Selection SliceTraits::select(const HyperSlabInterface& hyper_slab) const { const auto& slice = static_cast(*this); auto filespace = slice.getSpace(); filespace = hyper_slab.apply(filespace); - auto n_elements = detail::h5s_get_select_npoints(filespace.getId()); - auto memspace = DataSpace(std::array{size_t(n_elements)}); - - return detail::make_selection(memspace, filespace, details::get_dataset(slice)); -} - -template -template -inline Selection SliceTraits::select( - const RegularHyperSlabNoMalloc& hyper_slab) const { - const auto& slice = static_cast(*this); - auto filespace = slice.getSpace(); - filespace = hyper_slab.apply(filespace); - - const auto packed_dims = hyper_slab.packedDims(); - const auto n_elements = - std::accumulate(packed_dims.begin(), - packed_dims.end(), - size_t{1}, - [](const size_t& a, const hsize_t& b) { return a * b; }); - auto memspace = DataSpace(std::array{n_elements}); + const auto n_elements = detail::h5s_get_select_npoints(filespace.getId()); + auto memspace = DataSpace{static_cast(n_elements)}; return detail::make_selection(memspace, filespace, details::get_dataset(slice)); } diff --git a/src/examples/select_partial_dataset_cpp11.cpp b/src/examples/select_partial_dataset_cpp11.cpp index 65a2aa7d..1e480c16 100644 --- a/src/examples/select_partial_dataset_cpp11.cpp +++ b/src/examples/select_partial_dataset_cpp11.cpp @@ -34,11 +34,6 @@ int main(void) { dataset.write(values); // now we read back 2x2 values after an offset of 0x2 - { - std::vector result; - dataset.select(RegularHyperSlabNoMalloc<2>{{0, 2}, {2, 2}}).read(result); - } - std::vector> result; dataset.select({0, 2}, {2, 2}).read(result); diff --git a/src/examples/select_partial_dataset_cpp17.cpp b/src/examples/select_partial_dataset_cpp17.cpp new file mode 100644 index 00000000..96fe0e12 --- /dev/null +++ b/src/examples/select_partial_dataset_cpp17.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c), 2017, Adrien Devresse + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include +#include + +#include + +namespace { +template +struct RegularHyperSlabNoMalloc { + constexpr size_t rank() const { + return Rank; + } + + /// Dimensions when all gaps are removed. + constexpr std::array packedDims() const { + auto dims = std::array{}; + + for (size_t i = 0; i < Rank; ++i) { + dims[i] = count[i] * (block ? (*block)[i] : 1); + } + + return dims; + } + + HighFive::DataSpace apply(const HighFive::DataSpace& space_) const { + auto space = space_.clone(); + const auto error_code = H5Sselect_hyperslab(space.getId(), + H5S_SELECT_SET, + offset.data(), + stride ? stride->data() : nullptr, + count.data(), + block ? block->data() : nullptr); + + if (error_code < 0) { + HighFive::HDF5ErrMapper::ToException( + "Unable to select hyperslab"); + } + return space; + } + + std::array offset{}; + std::array count{}; + std::optional> stride{std::nullopt}; + std::optional> block{std::nullopt}; +}; +} // namespace + +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + // + // Note: In C++14, using braces in constructor implies "explicit" keyword. Compiler logs will + // warn about possible implicit type conversion that may involves transient memory allocations. + File file{"select_partial_example_cpp17.h5", File::ReadWrite | File::Create | File::Truncate}; + + // we have some example values in a 2D vector 2x5 + // Specifying the inner dimensions as std::array ensures that the two-dimensional data is + // contiguous, so that the compiler will only invoke one single memory allocation. The + // compiler also ensures the column counts are identical. + // + // Note: C++17 required. + const std::vector values{std::array{1.0, 2.0, 4.0, 8.0, 16.0}, + {32.0, 64.0, 128.0, 256.0, 512.0}}; + + // let's create a dataset of this size + DataSet dataset = file.createDataSet("dset", DataSpace::From(values)); + // and write them + dataset.write(values); + + + // now we read back 2x2 values after an offset of 0x2 + // Notice that std::array lives in the stack space memory. No memory allocation required. + std::array, 2> result; + + // Specify the selection without any memory allocations. + dataset.select(RegularHyperSlabNoMalloc<2>{{0, 2}, {2, 2}}).read_raw(result.front().data()); + + // we print out 4 values + for (auto i: result) { + for (auto j: i) { + std::cout << ' ' << j; + } + std::cout << '\n'; + } + + return 0; // successfully terminated +} From 0879657d2cb9f96efa530ddce408472913c5a1b6 Mon Sep 17 00:00:00 2001 From: Antony Chan Date: Thu, 11 Dec 2025 10:30:40 -0800 Subject: [PATCH 3/4] Move std::optional to example folder --- include/highfive/bits/H5Slice_traits.hpp | 3 --- src/examples/select_partial_dataset_cpp17.cpp | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/highfive/bits/H5Slice_traits.hpp b/include/highfive/bits/H5Slice_traits.hpp index 58f9ea6d..db287776 100644 --- a/include/highfive/bits/H5Slice_traits.hpp +++ b/include/highfive/bits/H5Slice_traits.hpp @@ -10,9 +10,6 @@ #include #include -#if __cplusplus >= 201703L -#include -#endif #include "H5_definitions.hpp" #include "H5Utils.hpp" diff --git a/src/examples/select_partial_dataset_cpp17.cpp b/src/examples/select_partial_dataset_cpp17.cpp index 96fe0e12..628bd3c8 100644 --- a/src/examples/select_partial_dataset_cpp17.cpp +++ b/src/examples/select_partial_dataset_cpp17.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include From c00d39aea07637f6ab8f88b64b01e998db6634f1 Mon Sep 17 00:00:00 2001 From: Luc Grosheintz Date: Fri, 12 Dec 2025 13:56:52 +0100 Subject: [PATCH 4/4] fixup: review comments. The changes are: - Make HyperSlabInterface an CRTP base class. - Make the example shorter, C++14 and rename it. --- include/highfive/bits/H5Dataspace_misc.hpp | 2 +- include/highfive/bits/H5Slice_traits.hpp | 17 +++- include/highfive/bits/H5Slice_traits_misc.hpp | 4 +- src/examples/CMakeLists.txt | 1 + src/examples/select_partial_dataset_cpp17.cpp | 98 ------------------- .../select_partial_dataset_no_alloc.cpp | 76 ++++++++++++++ 6 files changed, 94 insertions(+), 104 deletions(-) delete mode 100644 src/examples/select_partial_dataset_cpp17.cpp create mode 100644 src/examples/select_partial_dataset_no_alloc.cpp diff --git a/include/highfive/bits/H5Dataspace_misc.hpp b/include/highfive/bits/H5Dataspace_misc.hpp index b20a4914..17a44a65 100644 --- a/include/highfive/bits/H5Dataspace_misc.hpp +++ b/include/highfive/bits/H5Dataspace_misc.hpp @@ -42,7 +42,7 @@ inline DataSpace::DataSpace(const std::initializer_list& items) template inline DataSpace::DataSpace(size_t dim1, Args... dims) - : DataSpace(std::array{dim1, static_cast(dims)...}) {} + : DataSpace(std::array{dim1, static_cast(dims)...}) {} template inline DataSpace::DataSpace(const IT begin, const IT end) { diff --git a/include/highfive/bits/H5Slice_traits.hpp b/include/highfive/bits/H5Slice_traits.hpp index db287776..59f44a54 100644 --- a/include/highfive/bits/H5Slice_traits.hpp +++ b/include/highfive/bits/H5Slice_traits.hpp @@ -60,6 +60,17 @@ inline std::vector toSTLSizeVector(const std::vector& from) { return detail::convertSizeVector(from); } +/// +/// \brief A CRTP base class for hyper slab-like objects. +/// +template +class HyperSlabInterface { + public: + DataSpace apply(const DataSpace& space) const { + return static_cast(*this).apply(space); + } +}; + struct RegularHyperSlab { RegularHyperSlab() = default; @@ -108,7 +119,7 @@ struct RegularHyperSlab { std::vector block; }; -class HyperSlab { +class HyperSlab: public HyperSlabInterface { public: HyperSlab() { selects.emplace_back(RegularHyperSlab{}, Op::None); @@ -427,8 +438,8 @@ class SliceTraits { /// nicely into a multi-dimensional array, but only a subset of such an array. /// /// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays. - template - Selection select(const HyperSlabInterface& hyper_slab) const; + template + Selection select(const HyperSlabInterface& hyper_slab) const; /// /// \brief Select an \p hyper_slab in the current Slice/Dataset. diff --git a/include/highfive/bits/H5Slice_traits_misc.hpp b/include/highfive/bits/H5Slice_traits_misc.hpp index 87943dd9..db225b6b 100644 --- a/include/highfive/bits/H5Slice_traits_misc.hpp +++ b/include/highfive/bits/H5Slice_traits_misc.hpp @@ -229,8 +229,8 @@ inline Selection SliceTraits::select(const HyperSlab& hyper_slab, } template -template -inline Selection SliceTraits::select(const HyperSlabInterface& hyper_slab) const { +template +inline Selection SliceTraits::select(const HyperSlabInterface& hyper_slab) const { const auto& slice = static_cast(*this); auto filespace = slice.getSpace(); filespace = hyper_slab.apply(filespace); diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index a445b606..2ea96ff9 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -17,6 +17,7 @@ set(core_examples ${CMAKE_CURRENT_SOURCE_DIR}/renaming_objects.cpp ${CMAKE_CURRENT_SOURCE_DIR}/select_by_id_dataset_cpp11.cpp ${CMAKE_CURRENT_SOURCE_DIR}/select_partial_dataset_cpp11.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/select_partial_dataset_no_alloc.cpp ) set(span_examples diff --git a/src/examples/select_partial_dataset_cpp17.cpp b/src/examples/select_partial_dataset_cpp17.cpp deleted file mode 100644 index 628bd3c8..00000000 --- a/src/examples/select_partial_dataset_cpp17.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c), 2017, Adrien Devresse - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - */ -#include -#include -#include -#include -#include - -#include - -namespace { -template -struct RegularHyperSlabNoMalloc { - constexpr size_t rank() const { - return Rank; - } - - /// Dimensions when all gaps are removed. - constexpr std::array packedDims() const { - auto dims = std::array{}; - - for (size_t i = 0; i < Rank; ++i) { - dims[i] = count[i] * (block ? (*block)[i] : 1); - } - - return dims; - } - - HighFive::DataSpace apply(const HighFive::DataSpace& space_) const { - auto space = space_.clone(); - const auto error_code = H5Sselect_hyperslab(space.getId(), - H5S_SELECT_SET, - offset.data(), - stride ? stride->data() : nullptr, - count.data(), - block ? block->data() : nullptr); - - if (error_code < 0) { - HighFive::HDF5ErrMapper::ToException( - "Unable to select hyperslab"); - } - return space; - } - - std::array offset{}; - std::array count{}; - std::optional> stride{std::nullopt}; - std::optional> block{std::nullopt}; -}; -} // namespace - -int main(void) { - using namespace HighFive; - - // Create a new file using the default property lists. - // - // Note: In C++14, using braces in constructor implies "explicit" keyword. Compiler logs will - // warn about possible implicit type conversion that may involves transient memory allocations. - File file{"select_partial_example_cpp17.h5", File::ReadWrite | File::Create | File::Truncate}; - - // we have some example values in a 2D vector 2x5 - // Specifying the inner dimensions as std::array ensures that the two-dimensional data is - // contiguous, so that the compiler will only invoke one single memory allocation. The - // compiler also ensures the column counts are identical. - // - // Note: C++17 required. - const std::vector values{std::array{1.0, 2.0, 4.0, 8.0, 16.0}, - {32.0, 64.0, 128.0, 256.0, 512.0}}; - - // let's create a dataset of this size - DataSet dataset = file.createDataSet("dset", DataSpace::From(values)); - // and write them - dataset.write(values); - - - // now we read back 2x2 values after an offset of 0x2 - // Notice that std::array lives in the stack space memory. No memory allocation required. - std::array, 2> result; - - // Specify the selection without any memory allocations. - dataset.select(RegularHyperSlabNoMalloc<2>{{0, 2}, {2, 2}}).read_raw(result.front().data()); - - // we print out 4 values - for (auto i: result) { - for (auto j: i) { - std::cout << ' ' << j; - } - std::cout << '\n'; - } - - return 0; // successfully terminated -} diff --git a/src/examples/select_partial_dataset_no_alloc.cpp b/src/examples/select_partial_dataset_no_alloc.cpp new file mode 100644 index 00000000..504dab3c --- /dev/null +++ b/src/examples/select_partial_dataset_no_alloc.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c), 2025, Antony Chan + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + */ +#include +#include +#include +#include + +#include + +namespace { +template +struct RegularHyperSlabNoMalloc + : public HighFive::HyperSlabInterface> { + RegularHyperSlabNoMalloc(const std::array& offset_, + const std::array& count_) + : offset(offset_) + , count(count_) {} + + HighFive::DataSpace apply(const HighFive::DataSpace& space_) const { + auto space = space_.clone(); + const auto error_code = H5Sselect_hyperslab( + space.getId(), H5S_SELECT_SET, offset.data(), nullptr, count.data(), nullptr); + + if (error_code < 0) { + HighFive::HDF5ErrMapper::ToException( + "Unable to select hyperslab"); + } + return space; + } + + std::array offset{}; + std::array count{}; +}; +} // namespace + +int main(void) { + using namespace HighFive; + + // Create a new file using the default property lists. + File file{"select_partial_example_no_alloc.h5", File::Truncate}; + + // Example values in a 2x5 array. This might cause one allocation. + const std::vector> values{ + std::array{1.0, 2.0, 4.0, 8.0, 16.0}, + std::array{32.0, 64.0, 128.0, 256.0, 512.0}}; + + // Might cause allocations in HDF5 and for std::string if "dset" is too long for + // short-string optimization. + DataSet dataset = file.createDataSet("dset", DataSpace::From(values)); + + // ------------------------------------------- + // From here on: no allocations are permitted. + dataset.write(values); + + // Select the 2x2 values after an offset of 0x2. + std::array, 2> result; + + // Specify the selection without any memory allocations. + dataset.select(RegularHyperSlabNoMalloc<2>{{0, 2}, {2, 2}}).read_raw(result.front().data()); + + // Print out the 4 values. + for (auto i: result) { + for (auto j: i) { + std::cout << ' ' << j; + } + std::cout << '\n'; + } + + return 0; +}