Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/highfive/H5DataSpace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class DataSpace: public Object {
/// \endcode
/// \since 2.3
template <size_t N>
explicit DataSpace(const std::array<size_t, N>& dims);
constexpr explicit DataSpace(const std::array<size_t, N>& dims);

/// \brief Create a DataSpace of N-dimensions from an initializer list.
/// \param dims Dimensions of the new DataSpace
Expand Down
9 changes: 6 additions & 3 deletions include/highfive/bits/H5Dataspace_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,18 @@ inline DataSpace::DataSpace(const std::vector<size_t>& dims)
: DataSpace(dims.begin(), dims.end()) {}

template <size_t N>
inline DataSpace::DataSpace(const std::array<size_t, N>& dims)
: DataSpace(dims.begin(), dims.end()) {}
constexpr DataSpace::DataSpace(const std::array<size_t, N>& dims) {
std::array<hsize_t, N> real_dims;
std::copy(dims.begin(), dims.end(), real_dims.begin());
_hid = detail::h5s_create_simple(static_cast<int>(N), real_dims.data(), nullptr);
}

inline DataSpace::DataSpace(const std::initializer_list<size_t>& items)
: DataSpace(std::vector<size_t>(items)) {}

template <typename... Args>
inline DataSpace::DataSpace(size_t dim1, Args... dims)
: DataSpace(std::vector<size_t>{dim1, static_cast<size_t>(dims)...}) {}
: DataSpace(std::array<size_t, 1 + sizeof...(dims)>{dim1, static_cast<size_t>(dims)...}) {}

template <class IT, typename>
inline DataSpace::DataSpace(const IT begin, const IT end) {
Expand Down
17 changes: 14 additions & 3 deletions include/highfive/bits/H5Slice_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ inline std::vector<size_t> toSTLSizeVector(const std::vector<hsize_t>& from) {
return detail::convertSizeVector<size_t>(from);
}

///
/// \brief A CRTP base class for hyper slab-like objects.
///
template <typename Impl>
class HyperSlabInterface {
public:
DataSpace apply(const DataSpace& space) const {
return static_cast<const Impl&>(*this).apply(space);
}
};

struct RegularHyperSlab {
RegularHyperSlab() = default;

Expand Down Expand Up @@ -108,7 +119,7 @@ struct RegularHyperSlab {
std::vector<hsize_t> block;
};

class HyperSlab {
class HyperSlab: public HyperSlabInterface<HyperSlab> {
public:
HyperSlab() {
selects.emplace_back(RegularHyperSlab{}, Op::None);
Expand Down Expand Up @@ -416,7 +427,6 @@ class ProductSet {
friend class SliceTraits;
};


template <typename Derivate>
class SliceTraits {
public:
Expand All @@ -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 <class Impl>
Selection select(const HyperSlabInterface<Impl>& hyper_slab) const;

///
/// \brief Select an \p hyper_slab in the current Slice/Dataset.
Expand Down
8 changes: 4 additions & 4 deletions include/highfive/bits/H5Slice_traits_misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,18 @@ inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab,
}

template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab) const {
template <typename Impl>
inline Selection SliceTraits<Derivate>::select(const HyperSlabInterface<Impl>& hyper_slab) const {
const auto& slice = static_cast<const Derivate&>(*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, 1>{size_t(n_elements)});
const auto n_elements = detail::h5s_get_select_npoints(filespace.getId());
auto memspace = DataSpace{static_cast<size_t>(n_elements)};

return detail::make_selection(memspace, filespace, details::get_dataset(slice));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If RegularHyperSlabNoMalloc where user defined, I don't know how to solve this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you might have noticed I'm not so keen on having both a statically sized version and fixed-sized version. Especially, because it's currently not done for performance reasons and HighFive is very consistent about using std::vector for shapes.

Now for the good news: we could use SFINAE for select allowing users to pass in any type for which hyper_slab.apply(filespace) is valid (and maybe something else). Once, HighFive can accept anything "slab-like", there's no need to implement select specifically for RegularHyperSlabNoMalloc. For now RegularHyperSlabNoMalloc could live outside of HighFive (or in an example). That way even if you fork HighFive, rebasing your changes would be trivial, e.g. if you put RegularHyperSlabNoMalloc in a separate header, the chance of a merge conflict seems zero. Also, not adding it now doesn't mean we can't ever add it.

@antonysigma Would this work for you? The SFINAE part isn't strictly needed, in a first version an unconstrained template parameter would suffice. That way if you don't feel like fighting SFINAE, I can take care of it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: moving RegularHyperSlabNoMalloc<Rank> to src/examples/ folder. For sure! Please check out the new changes.

Re: SFINAE for select(const T& hyper_slab). Sounds good to me. Please review the new changes following your advises. I suppose the system can tolerate the additional H5Sget_select_npoints calls for now. Once HighFive project migrates to c++17, we have more advanced tricks to compute npoints at compile-time.

Would this work for you? The SFINAE part isn't strictly needed, in a first version an unconstrained template parameter would suffice. That way if you don't feel like fighting SFINAE, I can take care of it later.

Yes, help wanted to tighten the constraints eventually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll first try with H5Sget_select_npoints if profiling/analysis shows it's bad, then computing the size of the hyperslab will become (optional if need be) part of the HyperSlab interface.



template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& offset,
const std::vector<size_t>& count,
Expand Down
1 change: 1 addition & 0 deletions src/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 76 additions & 0 deletions src/examples/select_partial_dataset_no_alloc.cpp
Original file line number Diff line number Diff line change
@@ -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 <functional>
#include <iostream>
#include <string>
#include <vector>

#include <highfive/highfive.hpp>

namespace {
template <size_t Rank>
struct RegularHyperSlabNoMalloc
: public HighFive::HyperSlabInterface<RegularHyperSlabNoMalloc<Rank>> {
RegularHyperSlabNoMalloc(const std::array<hsize_t, Rank>& offset_,
const std::array<hsize_t, Rank>& 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<HighFive::DataSpaceException>(
"Unable to select hyperslab");
}
return space;
}

std::array<hsize_t, Rank> offset{};
std::array<hsize_t, Rank> 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<std::array<double, 5>> values{
std::array<double, 5>{1.0, 2.0, 4.0, 8.0, 16.0},
std::array<double, 5>{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<double>("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<std::array<double, 2>, 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;
}
Loading