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..17a44a65 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..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); @@ -416,7 +427,6 @@ class ProductSet { friend class SliceTraits; }; - template class SliceTraits { public: @@ -428,7 +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. - Selection select(const HyperSlab& 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 f451ac01..db225b6b 100644 --- a/include/highfive/bits/H5Slice_traits_misc.hpp +++ b/include/highfive/bits/H5Slice_traits_misc.hpp @@ -229,18 +229,18 @@ 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)}); + 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)); } - template inline Selection SliceTraits::select(const std::vector& offset, const std::vector& count, 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_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; +}