computeStats not working correctly, Unit Tests added

This commit is contained in:
rastogi 2025-09-01 12:39:01 +02:00
parent 12d39ecb9b
commit 0cc0c9cdf6
110 changed files with 20510 additions and 510 deletions

View File

@ -28,12 +28,12 @@ if (POET_PREPROCESS_BENCHS)
endif()
# as tug will also pull in doctest as a dependency
set(TUG_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(TUG_ENABLE_TESTING ON CACHE BOOL "" FORCE)
add_subdirectory(ext/tug EXCLUDE_FROM_ALL)
add_subdirectory(ext/iphreeqc EXCLUDE_FROM_ALL)
option(POET_ENABLE_TESTING "Build test suite for POET" OFF)
option(POET_ENABLE_TESTING "Build test suite for POET" ON)
if (POET_ENABLE_TESTING)
add_subdirectory(test)

7106
include/doctest/doctest.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
#ifndef DOCTEST_MPI_H
#define DOCTEST_MPI_H
#ifdef DOCTEST_CONFIG_IMPLEMENT
#include "doctest/extensions/mpi_sub_comm.h"
#include "mpi_reporter.h"
#include <unordered_map>
namespace doctest {
// Each time a MPI_TEST_CASE is executed on N procs,
// we need a sub-communicator of N procs to execute it.
// It is then registered here and can be re-used
// by other tests that requires a sub-comm of the same size
std::unordered_map<int,mpi_sub_comm> sub_comms_by_size;
// Record if at least one MPI_TEST_CASE was registered "skipped"
// because there is not enought procs to execute it
int nb_test_cases_skipped_insufficient_procs = 0;
std::string thread_level_to_string(int thread_lvl);
int mpi_init_thread(int argc, char *argv[], int required_thread_support);
void mpi_finalize();
// Can be safely called before MPI_Init()
// This is needed for MPI_TEST_CASE because we use doctest::skip()
// to prevent execution of tests where there is not enough procs,
// but doctest::skip() is called during test registration, that is, before main(), and hence before MPI_Init()
int mpi_comm_world_size() {
#if defined(OPEN_MPI)
const char* size_str = std::getenv("OMPI_COMM_WORLD_SIZE");
#elif defined(I_MPI_VERSION) || defined(MPI_VERSION) // Intel MPI + MPICH (at least)
const char* size_str = std::getenv("PMI_SIZE"); // see https://community.intel.com/t5/Intel-oneAPI-HPC-Toolkit/Environment-variables-defined-by-intel-mpirun/td-p/1096703
#else
#error "Unknown MPI implementation: please submit an issue or a PR to doctest. Meanwhile, you can look at the output of e.g. `mpirun -np 3 env` to search for an environnement variable that contains the size of MPI_COMM_WORLD and extend this code accordingly"
#endif
if (size_str==nullptr) return 1; // not launched with mpirun/mpiexec, so assume only one process
return std::stoi(size_str);
}
// Record size of MPI_COMM_WORLD with mpi_comm_world_size()
int world_size_before_init = mpi_comm_world_size();
std::string thread_level_to_string(int thread_lvl) {
switch (thread_lvl) {
case MPI_THREAD_SINGLE: return "MPI_THREAD_SINGLE";
case MPI_THREAD_FUNNELED: return "MPI_THREAD_FUNNELED";
case MPI_THREAD_SERIALIZED: return "MPI_THREAD_SERIALIZED";
case MPI_THREAD_MULTIPLE: return "MPI_THREAD_MULTIPLE";
default: return "Invalid MPI thread level";
}
}
int mpi_init_thread(int argc, char *argv[], int required_thread_support) {
int provided_thread_support;
MPI_Init_thread(&argc, &argv, required_thread_support, &provided_thread_support);
int world_size;
MPI_Comm_size(MPI_COMM_WORLD,&world_size);
if (world_size_before_init != world_size) {
DOCTEST_INTERNAL_ERROR(
"doctest found "+std::to_string(world_size_before_init)+" MPI processes before `MPI_Init_thread`,"
" but MPI_COMM_WORLD is actually of size "+std::to_string(world_size)+".\n"
"This is most likely due to your MPI implementation not being well supported by doctest. Please report this issue on GitHub"
);
}
if (provided_thread_support!=required_thread_support) {
std::cout <<
"WARNING: " + thread_level_to_string(required_thread_support) + " was asked, "
+ "but only " + thread_level_to_string(provided_thread_support) + " is provided by the MPI library\n";
}
return provided_thread_support;
}
void mpi_finalize() {
// We need to destroy all created sub-communicators before calling MPI_Finalize()
doctest::sub_comms_by_size.clear();
MPI_Finalize();
}
} // doctest
#else // DOCTEST_CONFIG_IMPLEMENT
#include "doctest/extensions/mpi_sub_comm.h"
#include <unordered_map>
#include <exception>
namespace doctest {
extern std::unordered_map<int,mpi_sub_comm> sub_comms_by_size;
extern int nb_test_cases_skipped_insufficient_procs;
extern int world_size_before_init;
int mpi_comm_world_size();
int mpi_init_thread(int argc, char *argv[], int required_thread_support);
void mpi_finalize();
template<int nb_procs, class F>
void execute_mpi_test_case(F func) {
auto it = sub_comms_by_size.find(nb_procs);
if (it==end(sub_comms_by_size)) {
bool was_emplaced = false;
std::tie(it,was_emplaced) = sub_comms_by_size.emplace(std::make_pair(nb_procs,mpi_sub_comm(nb_procs)));
assert(was_emplaced);
}
const mpi_sub_comm& sub = it->second;
if (sub.comm != MPI_COMM_NULL) {
func(sub.rank,nb_procs,sub.comm,std::integral_constant<int,nb_procs>{});
};
}
inline bool
insufficient_procs(int test_nb_procs) {
static const int world_size = mpi_comm_world_size();
bool insufficient = test_nb_procs>world_size;
if (insufficient) {
++nb_test_cases_skipped_insufficient_procs;
}
return insufficient;
}
} // doctest
#define DOCTEST_MPI_GEN_ASSERTION(rank_to_test, assertion, ...) \
static_assert(rank_to_test<test_nb_procs_as_int_constant.value,"Trying to assert on a rank greater than the number of procs of the test!"); \
if(rank_to_test == test_rank) assertion(__VA_ARGS__)
#define DOCTEST_MPI_WARN(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_WARN,__VA_ARGS__)
#define DOCTEST_MPI_CHECK(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_CHECK,__VA_ARGS__)
#define DOCTEST_MPI_REQUIRE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_REQUIRE,__VA_ARGS__)
#define DOCTEST_MPI_WARN_FALSE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_WARN_FALSE,__VA_ARGS__)
#define DOCTEST_MPI_CHECK_FALSE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_CHECK_FALSE,__VA_ARGS__)
#define DOCTEST_MPI_REQUIRE_FALSE(rank_to_test, ...) DOCTEST_MPI_GEN_ASSERTION(rank_to_test,DOCTEST_REQUIRE_FALSE,__VA_ARGS__)
#define DOCTEST_CREATE_MPI_TEST_CASE(name,nb_procs,func) \
static void func(DOCTEST_UNUSED int test_rank, DOCTEST_UNUSED int test_nb_procs, DOCTEST_UNUSED MPI_Comm test_comm, DOCTEST_UNUSED std::integral_constant<int,nb_procs>); \
TEST_CASE(name * doctest::description("MPI_TEST_CASE") * doctest::skip(doctest::insufficient_procs(nb_procs))) { \
doctest::execute_mpi_test_case<nb_procs>(func); \
} \
static void func(DOCTEST_UNUSED int test_rank, DOCTEST_UNUSED int test_nb_procs, DOCTEST_UNUSED MPI_Comm test_comm, DOCTEST_UNUSED std::integral_constant<int,nb_procs> test_nb_procs_as_int_constant)
// DOC: test_rank, test_nb_procs, and test_comm are available UNDER THESE SPECIFIC NAMES in the body of the unit test
// DOC: test_nb_procs_as_int_constant is equal to test_nb_procs, but as a compile time value
// (used in CHECK-like macros to assert the checked rank exists)
#define DOCTEST_MPI_TEST_CASE(name,nb_procs) \
DOCTEST_CREATE_MPI_TEST_CASE(name,nb_procs,DOCTEST_ANONYMOUS(DOCTEST_MPI_FUNC))
// == SHORT VERSIONS OF THE MACROS
#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
#define MPI_WARN DOCTEST_MPI_WARN
#define MPI_CHECK DOCTEST_MPI_CHECK
#define MPI_REQUIRE DOCTEST_MPI_REQUIRE
#define MPI_WARN_FALSE DOCTEST_MPI_WARN_FALSE
#define MPI_CHECK_FALSE DOCTEST_MPI_CHECK_FALSE
#define MPI_REQUIRE_FALSE DOCTEST_MPI_REQUIRE_FALSE
#define MPI_TEST_CASE DOCTEST_MPI_TEST_CASE
#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
#endif // DOCTEST_CONFIG_IMPLEMENT
#endif // DOCTEST_MPI_H

View File

@ -0,0 +1,37 @@
//
// doctest_util.h - an accompanying extensions header to the main doctest.h header
//
// Copyright (c) 2016-2023 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT
//
// The documentation can be found at the library's page:
// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md
//
#ifndef DOCTEST_UTIL_H
#define DOCTEST_UTIL_H
#ifndef DOCTEST_LIBRARY_INCLUDED
#include "../doctest.h"
#endif
#include <memory>
#include <vector>
#include <string>
namespace doctest {
inline void applyCommandLine(doctest::Context& ctx, const std::vector<std::string>& args) {
auto doctest_args = std::make_unique<const char*[]>(args.size());
for (size_t i = 0; i < args.size(); ++i) {
doctest_args[i] = args[i].c_str();
}
ctx.applyCommandLine(args.size(), doctest_args.get());
}
} // namespace doctest
#endif // DOCTEST_UTIL_H

View File

@ -0,0 +1,271 @@
#ifndef DOCTEST_MPI_REPORTER_H
#define DOCTEST_MPI_REPORTER_H
// #include <doctest/doctest.h>
#include <fstream>
#include <string>
#include "mpi.h"
#include <vector>
#include <mutex>
namespace doctest {
extern int nb_test_cases_skipped_insufficient_procs;
int mpi_comm_world_size();
namespace {
// https://stackoverflow.com/a/11826666/1583122
struct NullBuffer : std::streambuf {
int overflow(int c) { return c; }
};
class NullStream : public std::ostream {
public:
NullStream()
: std::ostream(&nullBuff)
{}
private:
NullBuffer nullBuff = {};
};
static NullStream nullStream;
/* \brief Extends the ConsoleReporter of doctest
* Each process writes its results to its own file
* Intended to be used when a test assertion fails and the user wants to know exactly what happens on which process
*/
struct MpiFileReporter : public ConsoleReporter {
std::ofstream logfile_stream = {};
MpiFileReporter(const ContextOptions& co)
: ConsoleReporter(co,logfile_stream)
{
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
std::string logfile_name = "doctest_" + std::to_string(rank) + ".log";
logfile_stream = std::ofstream(logfile_name.c_str(), std::fstream::out);
}
};
/* \brief Extends the ConsoleReporter of doctest
* Allows to manage the execution of tests in a parallel framework
* All results are collected on rank 0
*/
struct MpiConsoleReporter : public ConsoleReporter {
private:
static std::ostream& replace_by_null_if_not_rank_0(std::ostream* os) {
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank==0) {
return *os;
} else {
return nullStream;
}
}
std::vector<std::pair<std::string, int>> m_failure_str_queue = {};
public:
MpiConsoleReporter(const ContextOptions& co)
: ConsoleReporter(co,replace_by_null_if_not_rank_0(co.cout))
{}
std::string file_line_to_string(const char* file, int line,
const char* tail = ""){
std::stringstream ss;
ss << skipPathFromFilename(file)
<< (opt.gnu_file_line ? ":" : "(")
<< (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option
<< (opt.gnu_file_line ? ":" : "):") << tail;
return ss.str();
}
void test_run_end(const TestRunStats& p) override {
ConsoleReporter::test_run_end(p);
const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
// -----------------------------------------------------
// > Gather information in rank 0
int n_rank, rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &n_rank);
int g_numAsserts = 0;
int g_numAssertsFailed = 0;
int g_numTestCasesFailed = 0;
MPI_Reduce(&p.numAsserts , &g_numAsserts , 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&p.numAssertsFailed , &g_numAssertsFailed , 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&p.numTestCasesFailed, &g_numTestCasesFailed, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
std::vector<int> numAssertsFailedByRank;
if(rank == 0){
numAssertsFailedByRank.resize(static_cast<std::size_t>(n_rank));
}
MPI_Gather(&p.numAssertsFailed, 1, MPI_INT, numAssertsFailedByRank.data(), 1, MPI_INT, 0, MPI_COMM_WORLD);
if(rank == 0) {
separator_to_stream();
s << Color::Cyan << "[doctest] " << Color::None << "assertions on all processes: " << std::setw(6)
<< g_numAsserts << " | "
<< ((g_numAsserts == 0 || anythingFailed) ? Color::None : Color::Green)
<< std::setw(6) << (g_numAsserts - g_numAssertsFailed) << " passed" << Color::None
<< " | " << (g_numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6)
<< g_numAssertsFailed << " failed" << Color::None << " |\n";
if (nb_test_cases_skipped_insufficient_procs>0) {
s << Color::Cyan << "[doctest] " << Color::Yellow << "WARNING: Skipped ";
if (nb_test_cases_skipped_insufficient_procs>1) {
s << nb_test_cases_skipped_insufficient_procs << " tests requiring more than ";
} else {
s << nb_test_cases_skipped_insufficient_procs << " test requiring more than ";
}
if (mpi_comm_world_size()>1) {
s << mpi_comm_world_size() << " MPI processes to run\n";
} else {
s << mpi_comm_world_size() << " MPI process to run\n";
}
}
separator_to_stream();
if(g_numAssertsFailed > 0){
s << Color::Cyan << "[doctest] " << Color::None << "fail on rank:" << std::setw(6) << "\n";
for(std::size_t i = 0; i < numAssertsFailedByRank.size(); ++i){
if( numAssertsFailedByRank[i] > 0 ){
s << std::setw(16) << " -> On rank [" << i << "] with " << numAssertsFailedByRank[i] << " test failed" << std::endl;
}
}
}
s << Color::Cyan << "[doctest] " << Color::None
<< "Status: " << (g_numTestCasesFailed > 0 ? Color::Red : Color::Green)
<< ((g_numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl;
}
}
void test_case_end(const CurrentTestCaseStats& st) override {
if (is_mpi_test_case()) {
// function called by every rank at the end of a test
// if failed assertions happened, they have been sent to rank 0
// here rank zero gathers them and prints them all
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
std::vector<MPI_Request> requests;
requests.reserve(m_failure_str_queue.size()); // avoid realloc & copy of MPI_Request
for (const std::pair<std::string, int> &failure : m_failure_str_queue)
{
const std::string & failure_str = failure.first;
const int failure_line = failure.second;
int failure_msg_size = static_cast<int>(failure_str.size());
requests.push_back(MPI_REQUEST_NULL);
MPI_Isend(failure_str.c_str(), failure_msg_size, MPI_BYTE,
0, failure_line, MPI_COMM_WORLD, &requests.back()); // Tag = file line
}
// Compute the number of assert with fail among all procs
const int nb_fail_asserts = static_cast<int>(m_failure_str_queue.size());
int nb_fail_asserts_glob = 0;
MPI_Reduce(&nb_fail_asserts, &nb_fail_asserts_glob, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
if(rank == 0) {
MPI_Status status;
MPI_Status status_recv;
using id_string = std::pair<int,std::string>;
std::vector<id_string> msgs(static_cast<std::size_t>(nb_fail_asserts_glob));
for (std::size_t i=0; i<static_cast<std::size_t>(nb_fail_asserts_glob); ++i) {
MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
int count;
MPI_Get_count(&status, MPI_BYTE, &count);
std::string recv_msg(static_cast<std::size_t>(count),'\0');
void* recv_msg_data = const_cast<char*>(recv_msg.data()); // const_cast needed. Non-const .data() exists in C++11 though...
MPI_Recv(recv_msg_data, count, MPI_BYTE, status.MPI_SOURCE,
status.MPI_TAG, MPI_COMM_WORLD, &status_recv);
msgs[i] = {status.MPI_SOURCE,recv_msg};
}
std::sort(begin(msgs),end(msgs),[](const id_string& x, const id_string& y){ return x.first < y.first; });
// print
if (nb_fail_asserts_glob>0) {
separator_to_stream();
file_line_to_stream(tc->m_file.c_str(), static_cast<int>(tc->m_line), "\n");
if(tc->m_test_suite && tc->m_test_suite[0] != '\0')
s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n";
if(strncmp(tc->m_name, " Scenario:", 11) != 0)
s << Color::Yellow << "TEST CASE: ";
s << Color::None << tc->m_name << "\n\n";
for(const auto& msg : msgs) {
s << msg.second;
}
s << "\n";
}
}
MPI_Waitall(static_cast<int>(requests.size()), requests.data(), MPI_STATUSES_IGNORE);
m_failure_str_queue.clear();
}
ConsoleReporter::test_case_end(st);
}
bool is_mpi_test_case() const {
return tc->m_description != nullptr
&& std::string(tc->m_description) == std::string("MPI_TEST_CASE");
}
void log_assert(const AssertData& rb) override {
if (!is_mpi_test_case()) {
ConsoleReporter::log_assert(rb);
} else {
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if(!rb.m_failed && !opt.success)
return;
std::lock_guard<std::mutex> lock(mutex);
std::stringstream failure_msg;
failure_msg << Color::Red << "On rank [" << rank << "] : " << Color::None;
failure_msg << file_line_to_string(rb.m_file, rb.m_line, " ");
if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==0){
failure_msg << Color::Cyan
<< assertString(rb.m_at)
<< "( " << rb.m_expr << " ) "
<< Color::None
<< (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")
<< " values: "
<< assertString(rb.m_at)
<< "( " << rb.m_decomp.c_str() << " )\n";
}
m_failure_str_queue.push_back({failure_msg.str(), rb.m_line});
}
}
}; // MpiConsoleReporter
// "1" is the priority - used for ordering when multiple reporters/listeners are used
REGISTER_REPORTER("MpiConsoleReporter", 1, MpiConsoleReporter);
REGISTER_REPORTER("MpiFileReporter", 1, MpiFileReporter);
} // anonymous
} // doctest
#endif // DOCTEST_REPORTER_H

View File

@ -0,0 +1,84 @@
#ifndef DOCTEST_MPI_SUB_COMM_H
#define DOCTEST_MPI_SUB_COMM_H
#include "mpi.h"
#include "doctest/doctest.h"
#include <cassert>
#include <string>
namespace doctest {
inline
int mpi_world_nb_procs() {
int n;
MPI_Comm_size(MPI_COMM_WORLD, &n);
return n;
}
struct mpi_sub_comm {
int nb_procs;
int rank;
MPI_Comm comm;
mpi_sub_comm( mpi_sub_comm const& ) = delete;
mpi_sub_comm& operator=( mpi_sub_comm const& ) = delete;
mpi_sub_comm(int nb_prcs) noexcept
: nb_procs(nb_prcs)
, rank(-1)
, comm(MPI_COMM_NULL)
{
int comm_world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &comm_world_rank);
if (nb_procs>mpi_world_nb_procs()) {
if (comm_world_rank==0) {
MESSAGE(
"Unable to run test: need ", std::to_string(nb_procs), " procs",
" but program launched with only ", std::to_string(doctest::mpi_world_nb_procs()), "."
);
CHECK(nb_procs<=mpi_world_nb_procs());
}
} else {
int color = MPI_UNDEFINED;
if(comm_world_rank < nb_procs){
color = 0;
}
MPI_Comm_split(MPI_COMM_WORLD, color, comm_world_rank, &comm);
if(comm != MPI_COMM_NULL){
MPI_Comm_rank(comm, &rank);
assert(rank==comm_world_rank);
}
}
}
void destroy_comm() {
if(comm != MPI_COMM_NULL){
MPI_Comm_free(&comm);
}
}
mpi_sub_comm(mpi_sub_comm&& x)
: nb_procs(x.nb_procs)
, rank(x.rank)
, comm(x.comm)
{
x.comm = MPI_COMM_NULL;
}
mpi_sub_comm& operator=(mpi_sub_comm&& x) {
destroy_comm();
nb_procs = x.nb_procs;
rank = x.rank;
comm = x.comm;
x.comm = MPI_COMM_NULL;
return *this;
}
~mpi_sub_comm() {
destroy_comm();
}
};
} // doctest
#endif // DOCTEST_SUB_COMM_H

View File

@ -0,0 +1,295 @@
/*
* Copyright (c), 2017, Ali Can Demiralp <ali.demiralp@rwth-aachen.de>
*
* 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)
*
*/
#pragma once
#include <vector>
#include <H5Apublic.h>
#include "H5DataType.hpp"
#include "H5DataSpace.hpp"
#include "H5Object.hpp"
#include "bits/H5Friends.hpp"
#include "bits/H5Path_traits.hpp"
namespace HighFive {
class DataSpace;
namespace detail {
/// \brief Internal hack to create an `Attribute` from an ID.
///
/// WARNING: Creating an Attribute from an ID has implications w.r.t. the lifetime of the object
/// that got passed via its ID. Using this method careless opens up the suite of issues
/// related to C-style resource management, including the analog of double free, dangling
/// pointers, etc.
///
/// NOTE: This is not part of the API and only serves to work around a compiler issue in GCC which
/// prevents us from using `friend`s instead. This function should only be used for internal
/// purposes. The problematic construct is:
///
/// template<class Derived>
/// friend class SomeCRTP<Derived>;
///
/// \private
Attribute make_attribute(hid_t hid);
} // namespace detail
/// \brief Class representing an Attribute of a DataSet or Group
///
/// \sa AnnotateTraits::createAttribute, AnnotateTraits::getAttribute, AnnotateTraits::listAttributeNames, AnnotateTraits::hasAttribute, AnnotateTraits::deleteAttribute for create, get, list, check or delete Attribute
class Attribute: public Object, public PathTraits<Attribute> {
public:
const static ObjectType type = ObjectType::Attribute;
/// \brief Get the name of the current Attribute.
/// \code{.cpp}
/// auto attr = dset.createAttribute<std::string>("my_attribute", DataSpace::From(string_list));
/// std::cout << attr.getName() << std::endl; // Will print "my_attribute"
/// \endcode
/// \since 2.2.2
std::string getName() const;
/// \brief The number of bytes required to store the attribute in the HDF5 file.
/// \code{.cpp}
/// size_t size = dset.createAttribute<int>("foo", DataSpace(1, 2)).getStorageSize();
/// \endcode
/// \since 1.0
size_t getStorageSize() const;
/// \brief Get the DataType of the Attribute.
/// \code{.cpp}
/// Attribute attr = dset.createAttribute<int>("foo", DataSpace(1, 2));
/// auto dtype = attr.getDataType(); // Will be an hdf5 type deduced from int
/// \endcode
/// \since 1.0
DataType getDataType() const;
/// \brief Get a copy of the DataSpace of the current Attribute.
/// \code{.cpp}
/// Attribute attr = dset.createAttribute<int>("foo", DataSpace(1, 2));
/// auto dspace = attr.getSpace(); // This will be a DataSpace of dimension 1 * 2
/// \endcode
/// \since 1.0
DataSpace getSpace() const;
/// \brief Get the memory DataSpace of the current Attribute.
///
/// HDF5 attributes don't support selections. Therefore, there's no need
/// for a memory dataspace. However, HighFive supports allocating arrays
/// and checking dimensions, this requires the dimensions of the memspace.
///
/// \since 1.0
DataSpace getMemSpace() const;
/// \brief Get the value of the Attribute.
/// \code{.cpp}
/// Attribute attr = dset.getAttribute("foo");
/// // The value will contains what have been written in the attribute
/// std::vector<int> value = attr.read<std::vector<int>>();
/// \endcode
/// \since 2.5.0
template <typename T>
T read() const;
/// \brief Get the value of the Attribute in a buffer.
///
/// Read the attribute into an existing object. Only available for
/// supported types `T`. If `array` has preallocated the correct amount of
/// memory, then this routine should not trigger reallocation. Otherwise,
/// if supported, the object will be resized.
///
/// An exception is raised if the numbers of dimension of the buffer and of
/// the attribute are different.
///
/// \code{.cpp}
/// // Will read into `value` avoiding memory allocation if the dimensions
/// // match, i.e. if the attribute `"foo"` has three element.
/// std::vector<int> value(3);
/// file.getAttribute("foo").read(value);
/// \endcode
/// \since 1.0
template <typename T>
void read(T& array) const;
/// \brief Read the attribute into a pre-allocated buffer.
/// \param array A pointer to the first byte of sufficient pre-allocated memory.
/// \param mem_datatype The DataType of the array.
///
/// \note This is the shallowest wrapper around `H5Aread`. If possible
/// prefer either Attribute::read() const or Attribute::read(T&) const.
///
/// \code{.cpp}
/// auto attr = file.getAttribute("foo");
///
/// // Simulate custom allocation by the application.
/// size_t n_elements = attr.getSpace().getElementCount();
/// int * ptr = (int*) malloc(n_elements*sizeof(int));
///
/// // Read into the pre-allocated memory.
/// attr.read(ptr, mem_datatype);
/// \endcode
/// \since 2.2.2
template <typename T>
void read_raw(T* array, const DataType& mem_datatype) const;
/// \brief Read the attribute into a buffer.
/// Behaves like Attribute::read(T*, const DataType&) const but
/// additionally this overload deduces the memory datatype from `T`.
///
/// \param array Pointer to the first byte of pre-allocated memory.
///
/// \note If possible prefer either Attribute::read() const or Attribute::read(T&) const.
///
/// \code{.cpp}
/// auto attr = file.getAttribute("foo");
///
/// // Simulate custom allocation by the application.
/// size_t n_elements = attr.getSpace().getElementCount();
/// int * ptr = (int*) malloc(n_elements*sizeof(int));
///
/// // Read into the pre-allocated memory.
/// attr.read(ptr);
/// \endcode
/// \since 2.2.2
template <typename T>
void read_raw(T* array) const;
/// \brief Write the value into the Attribute.
///
/// Write the value to the attribute. For supported types `T`, this overload
/// will write the value to the attribute. The datatype and dataspace are
/// deduced automatically. However, since the attribute has already been
/// created, the dimensions of `value` must match those of the attribute.
///
/// \code{.cpp}
/// // Prefer the fused version if creating and writing the attribute
/// // at the same time.
/// dset.createAttribute("foo", std::vector<int>{1, 2, 3});
///
/// // To overwrite the value:
/// std::vector<int> value{4, 5, 6};
/// dset.getAttribute<int>("foo").write(value);
/// \endcode
/// \since 1.0
template <typename T>
void write(const T& value);
/// \brief Write from a raw pointer.
///
/// Values that have been correctly arranged memory, can be written directly
/// by passing a raw pointer.
///
/// \param buffer Pointer to the first byte of the value.
/// \param mem_datatype The DataType of the buffer.
///
/// \note This is the shallowest wrapper around `H5Awrite`. It's useful
/// if you need full control. If possible prefer Attribute::write.
///
/// \code{.cpp}
/// Attribute attr = dset.createAttribute<int>("foo", DataSpace(2, 3));
///
/// // Simulate the application creating `value` and only exposing access
/// // to the raw pointer `ptr`.
/// std::vector<std::array<int, 3>> value{{1, 2, 3}, {4, 5, 6}};
/// int * ptr = (int*) value.data();
///
/// // Simply write the bytes to disk.
/// attr.write(ptr, AtomicType<int>());
/// \endcode
/// \since 2.2.2
template <typename T>
void write_raw(const T* buffer, const DataType& mem_datatype);
/// \brief Write from a raw pointer.
///
/// Much like Attribute::write_raw(const T*, const DataType&).
/// Additionally, this overload attempts to automatically deduce the
/// datatype of the buffer. Note, that the file datatype is already set.
///
/// \param buffer Pointer to the first byte.
///
/// \note If possible prefer Attribute::write.
///
/// \code{.cpp}
/// // Simulate the application creating `value` and only exposing access
/// // to the raw pointer `ptr`.
/// std::vector<std::array<int, 3>> value{{1, 2, 3}, {4, 5, 6}};
/// int * ptr = (int*) value.data();
///
/// // Simply write the bytes to disk.
/// attr.write(ptr);
/// \endcode
/// \since 2.2.2
template <typename T>
void write_raw(const T* buffer);
/// \brief The create property list used for this attribute.
///
/// Some of HDF5 properties/setting of an attribute are defined by a
/// create property list. This method returns a copy of the create
/// property list used during creation of the attribute.
///
/// \code{.cpp}
/// auto acpl = attr.getCreatePropertyList();
///
/// // For example to create another attribute with the same properties.
/// file.createAttribute("foo", 42, acpl);
/// \endcode
/// \since 2.5.0
AttributeCreateProps getCreatePropertyList() const {
return details::get_plist<AttributeCreateProps>(*this, H5Aget_create_plist);
}
// No empty attributes
Attribute() = delete;
///
/// \brief Return an `Attribute` with `axes` squeezed from the memspace.
///
/// Returns an `Attribute` in which the memspace has been modified
/// to not include the axes listed in `axes`.
///
/// Throws if any axis to be squeezes has a dimension other than `1`.
///
/// \since 3.0
Attribute squeezeMemSpace(const std::vector<size_t>& axes) const;
///
/// \brief Return a `Attribute` with a simple memspace with `dims`.
///
/// Returns a `Attribute` in which the memspace has been modified
/// to be a simple dataspace with dimensions `dims`.
///
/// Throws if the number of elements changes.
///
/// \since 3.0
Attribute reshapeMemSpace(const std::vector<size_t>& dims) const;
protected:
using Object::Object;
private:
DataSpace _mem_space;
#if HIGHFIVE_HAS_FRIEND_DECLARATIONS
template <typename Derivate>
friend class ::HighFive::AnnotateTraits;
#endif
friend Attribute detail::make_attribute(hid_t);
};
namespace detail {
inline Attribute make_attribute(hid_t hid) {
return Attribute(hid);
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,114 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <vector>
#include "H5DataSpace.hpp"
#include "H5DataType.hpp"
#include "H5Object.hpp"
#include "bits/H5_definitions.hpp"
#include "bits/H5Annotate_traits.hpp"
#include "bits/H5Slice_traits.hpp"
#include "bits/H5Path_traits.hpp"
#include "bits/H5_definitions.hpp"
namespace HighFive {
///
/// \brief Class representing a dataset.
///
class DataSet: public Object,
public SliceTraits<DataSet>,
public AnnotateTraits<DataSet>,
public PathTraits<DataSet> {
public:
const static ObjectType type = ObjectType::Dataset;
///
/// \brief getStorageSize
/// \return returns the amount of storage allocated for a dataset.
///
uint64_t getStorageSize() const;
///
/// \brief getOffset
/// \return returns DataSet address in file
///
uint64_t getOffset() const;
///
/// \brief getDataType
/// \return return the datatype associated with this dataset
///
DataType getDataType() const;
///
/// \brief getSpace
/// \return return the dataspace associated with this dataset
///
DataSpace getSpace() const;
///
/// \brief getMemSpace
/// \return same than getSpace for DataSet, compatibility with Selection
/// class
///
DataSpace getMemSpace() const;
/// \brief Change the size of the dataset
///
/// This requires that the dataset was created with chunking, and you would
/// generally want to have set a larger maxdims setting
/// \param dims New size of the dataset
void resize(const std::vector<size_t>& dims);
/// \brief Get the dimensions of the whole DataSet.
/// This is a shorthand for getSpace().getDimensions()
/// \return The shape of the current HighFive::DataSet
///
inline std::vector<size_t> getDimensions() const {
return getSpace().getDimensions();
}
/// \brief Get the total number of elements in the current dataset.
/// E.g. 2x2x2 matrix has size 8.
/// This is a shorthand for getSpace().getTotalCount()
/// \return The shape of the current HighFive::DataSet
///
inline size_t getElementCount() const {
return getSpace().getElementCount();
}
/// \brief Get the list of properties for creation of this dataset
DataSetCreateProps getCreatePropertyList() const {
return details::get_plist<DataSetCreateProps>(*this, H5Dget_create_plist);
}
/// \brief Get the list of properties for accession of this dataset
DataSetAccessProps getAccessPropertyList() const {
return details::get_plist<DataSetAccessProps>(*this, H5Dget_access_plist);
}
DataSet() = default;
protected:
using Object::Object; // bring DataSet(hid_t)
explicit DataSet(Object&& o) noexcept
: Object(std::move(o)) {}
friend class Reference;
template <typename Derivate>
friend class NodeTraits;
};
} // namespace HighFive

View File

@ -0,0 +1,282 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <vector>
#include <array>
#include <cstdint>
#include <type_traits>
#include <initializer_list>
#include "H5Object.hpp"
#include "bits/H5_definitions.hpp"
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}
/// // Create a DataSpace of dimension 1 x 2 x 3
/// DataSpace dspace(1, 2, 3);
/// std::cout << dspace.getElementCount() << std::endl; // Print 1 * 2 * 3 = 6
/// std::cout << dspace.getNumberDimensions() << std::endl; // Print 3
/// std::vector<size_t> dims = dspace.getDimensions(); // dims is {1, 2, 3}
/// \endcode
class DataSpace: public Object {
public:
const static ObjectType type = ObjectType::DataSpace;
/// \brief Magic value to specify that a DataSpace can grow without limit.
///
/// This value should be used with DataSpace::DataSpace(const std::vector<size_t>& dims, const
/// std::vector<size_t>& maxdims);
///
/// \since 2.0
static const size_t UNLIMITED = SIZE_MAX;
/// \brief An enum to create scalar and null DataSpace with DataSpace::DataSpace(DataspaceType dtype).
///
/// This enum is needed otherwise we will not be able to distringuish between both with normal
/// constructors. Both have a dimension of 0.
/// \since 1.3
enum class DataspaceType {
dataspace_scalar, ///< Value to create scalar DataSpace
dataspace_null, ///< Value to create null DataSpace
// simple dataspace are handle directly from their dimensions
};
// For backward compatibility: `DataSpace::dataspace_scalar`.
constexpr static DataspaceType dataspace_scalar = DataspaceType::dataspace_scalar;
constexpr static DataspaceType dataspace_null = DataspaceType::dataspace_null;
/// \brief Create a DataSpace of N-dimensions from a std::vector<size_t>.
/// \param dims Dimensions of the new DataSpace
///
/// \code{.cpp}
/// // Create a DataSpace with 2 dimensions: 1 and 3
/// DataSpace(std::vector<size_t>{1, 3});
/// \endcode
/// \since 1.0
explicit DataSpace(const std::vector<size_t>& dims);
/// \brief Create a DataSpace of N-dimensions from a std::array<size_t, N>.
/// \param dims Dimensions of the new DataSpace
///
/// \code{.cpp}
/// // Create a DataSpace with 2 dimensions: 1 and 3
/// DataSpace(std::array<size_t, 2>{1, 3});
/// \endcode
/// \since 2.3
template <size_t N>
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
///
/// \code{.cpp}
/// // Create a DataSpace with 2 dimensions: 1 and 3
/// DataSpace{1, 3};
/// \endcode
/// \since 2.1
DataSpace(const std::initializer_list<size_t>& dims);
/// \brief Create a DataSpace of N-dimensions from direct values.
/// \param dim1 The first dimension
/// \param dims The following dimensions
///
/// \code{.cpp}
/// // Create a DataSpace with 2 dimensions: 1 and 3
/// DataSpace(1, 3);
/// \endcode
/// \since 2.1
template <typename... Args>
explicit DataSpace(size_t dim1, Args... dims);
/// \brief Create a DataSpace from a pair of iterators.
/// \param begin The beginning of the container
/// \param end The end of the container
///
/// \code{.cpp}
/// // Create a DataSpace with 2 dimensions: 1 and 3
/// std::vector<int> v{1, 3};
/// DataSpace(v.begin(), v.end());
/// \endcode
///
/// \since 2.0
// Attention: Explicitly disable DataSpace(int_like, int_like) from trying
// to use this constructor
template <typename IT,
typename = typename std::enable_if<!std::is_integral<IT>::value, IT>::type>
DataSpace(IT begin, IT end);
/// \brief Create a resizable N-dimensional DataSpace.
/// \param dims Initial size of dataspace
/// \param maxdims Maximum size of the dataspace
///
/// \code{.cpp}
/// // Create a DataSpace with 2 dimensions: 1 and 3.
/// // It can later be resized up to a maximum of 10 x 10
/// DataSpace(std::vector<size_t>{1, 3}, std::vector<size_t>{10, 10});
/// \endcode
///
/// \see UNLIMITED for a DataSpace that can be resized without limit.
/// \since 2.0
explicit DataSpace(const std::vector<size_t>& dims, const std::vector<size_t>& maxdims);
/// \brief Create a scalar or a null DataSpace.
///
/// This overload enables creating scalar or null data spaces, both have
/// dimension 0.
///
/// \param space_type The value from the enum
///
/// \code{.cpp}
/// DataSpace(DataspaceType::dataspace_scalar);
/// \endcode
///
/// \attention Avoid braced intialization in these cases, i.e.
/// \code{.cpp}
/// // This is not a scalar dataset:
/// DataSpace{DataspaceType::dataspace_scalar};
/// \endcode
///
/// \since 1.3
explicit DataSpace(DataspaceType space_type);
/// \brief Create a scalar DataSpace.
///
/// \code{.cpp}
/// auto dataspace = DataSpace::Scalar();
/// \endcode
///
/// \since 2.9
static DataSpace Scalar();
/// \brief Create a null DataSpace.
///
/// \code{.cpp}
/// auto dataspace = DataSpace::Null();
/// \endcode
///
/// \since 2.9
static DataSpace Null();
/// \brief Create a copy of the DataSpace which will have different id.
///
/// \code{.cpp}
/// DataSpace dspace1(1, 3);
/// auto dspace2 = dspace.clone();
/// \endcode
///
/// \since 1.0
DataSpace clone() const;
/// \brief Returns the number of dimensions of a DataSpace.
/// \code{.cpp}
/// DataSpace dspace(1, 3);
/// size_t number_of_dim = dspace.getNumberDimensions(); // returns 2
/// \endcode
/// \since 1.0
size_t getNumberDimensions() const;
/// \brief Returns the size of the dataset in each dimension.
///
/// For zero-dimensional datasets (e.g. scalar or null datasets) an empty
/// vector is returned.
///
/// \code{.cpp}
/// DataSpace dspace(1, 3);
/// auto dims = dspace.getDimensions(); // returns {1, 3}
/// \endcode
///
/// \sa DataSpace::getMaxDimensions
///
/// \since 1.0
std::vector<size_t> getDimensions() const;
/// \brief Return the number of elements in this DataSpace.
///
/// \code{.cpp}
/// DataSpace dspace(1, 3);
/// size_t elementcount = dspace.getElementCount(); // return 1 x 3 = 3
/// \endcode
/// \since 2.1
size_t getElementCount() const;
/// \brief Returns the maximum size of the dataset in each dimension.
///
/// This is the maximum size a dataset can be extended to, which may be
/// different from the current size of the dataset.
///
/// \code{.cpp}
/// DataSpace dspace(std::vector<size_t>{1, 3}, std::vector<size_t>{UNLIMITED, 10});
/// dspace.getMaxDimensions(); // Return {UNLIMITED, 10}
/// \endcode
///
/// \sa DataSpace::getDimensions
/// \since 2.0
std::vector<size_t> getMaxDimensions() const;
/// \brief Automatically deduce the DataSpace from a container/value.
///
/// Certain containers and scalar values are fully supported by HighFive.
/// For these containers, HighFive can deduce the dimensions from `value`.
///
/// \code{.cpp}
/// double d = 42.0;
/// std::vector<std::vector<int>> v = {{4, 5, 6}, {7, 8, 9}};
/// DataSpace::From(v); // A DataSpace of dimensions 2, 3.
/// DataSpace::From(d); // A scalar dataspace.
/// \endcode
///
/// \since 1.0
template <typename T>
static DataSpace From(const T& value);
/// \brief Create a DataSpace from a value of type string array.
/// \param string_array An C-array of C-string (null-terminated).
///
/// \code{.cpp}
/// char string_array[2][10] = {"123456789", "abcdefghi"};
/// auto dspace = DataSpace::FromCharArrayStrings(string_array); // dspace is a DataSpace of
/// dimensions 2
/// \endcode
/// \since 2.2
template <std::size_t N, std::size_t Width>
static DataSpace FromCharArrayStrings(const char (&string_array)[N][Width]);
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
// We include bits right away since DataSpace is user-constructible
#include "bits/H5Dataspace_misc.hpp"

View File

@ -0,0 +1,367 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <type_traits>
#include <vector>
#include <H5Tpublic.h>
#include "H5Object.hpp"
#include "bits/H5Utils.hpp"
#include "bits/string_padding.hpp"
#include "H5PropertyList.hpp"
#include "bits/h5_wrapper.hpp"
#include "bits/h5t_wrapper.hpp"
namespace HighFive {
///
/// \brief Enum of Fundamental data classes
///
enum class DataTypeClass {
Time = 1 << 1,
Integer = 1 << 2,
Float = 1 << 3,
String = 1 << 4,
BitField = 1 << 5,
Opaque = 1 << 6,
Compound = 1 << 7,
Reference = 1 << 8,
Enum = 1 << 9,
VarLen = 1 << 10,
Array = 1 << 11,
Invalid = 0
};
inline DataTypeClass operator|(DataTypeClass lhs, DataTypeClass rhs) {
using T = std::underlying_type<DataTypeClass>::type;
return static_cast<DataTypeClass>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
inline DataTypeClass operator&(DataTypeClass lhs, DataTypeClass rhs) {
using T = std::underlying_type<DataTypeClass>::type;
return static_cast<DataTypeClass>(static_cast<T>(lhs) & static_cast<T>(rhs));
}
class StringType;
///
/// \brief HDF5 Data Type
///
class DataType: public Object {
public:
bool operator==(const DataType& other) const;
bool operator!=(const DataType& other) const;
///
/// \brief Return the fundamental type.
///
DataTypeClass getClass() const;
///
/// \brief Returns the length (in bytes) of this type elements
///
/// Notice that the size of variable length sequences may have limited applicability
/// given that it refers to the size of the control structure. For info see
/// https://support.hdfgroup.org/HDF5/doc/RM/RM_H5T.html#Datatype-GetSize
size_t getSize() const;
///
/// \brief Returns a friendly description of the type (e.g. Float32)
///
std::string string() const;
///
/// \brief Returns whether the type is a variable-length string
///
bool isVariableStr() const;
///
/// \brief Returns whether the type is a fixed-length string
///
bool isFixedLenStr() const;
///
/// \brief Returns this datatype as a `StringType`.
///
StringType asStringType() const;
///
/// \brief Check the DataType was default constructed.
///
bool empty() const noexcept;
/// \brief Returns whether the type is a Reference
bool isReference() const;
/// \brief Get the list of properties for creation of this DataType
DataTypeCreateProps getCreatePropertyList() const {
return details::get_plist<DataTypeCreateProps>(*this, H5Tget_create_plist);
}
protected:
using Object::Object;
friend class Attribute;
friend class File;
friend class DataSet;
friend class CompoundType;
template <typename Derivate>
friend class NodeTraits;
};
enum class CharacterSet : std::underlying_type<H5T_cset_t>::type {
Ascii = H5T_CSET_ASCII,
Utf8 = H5T_CSET_UTF8,
};
class StringType: public DataType {
public:
///
/// \brief For stings return the character set.
///
CharacterSet getCharacterSet() const;
///
/// \brief For fixed length stings return the padding.
///
StringPadding getPadding() const;
protected:
using DataType::DataType;
friend class DataType;
};
class FixedLengthStringType: public StringType {
public:
///
/// \brief Create a fixed length string datatype.
///
/// The string will be `size` bytes long, regardless whether it's ASCII or
/// 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. 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
/// guarantee to the reader that it contains a `\0`. Therefore, make sure
/// 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,
CharacterSet character_set = CharacterSet::Ascii);
};
class VariableLengthStringType: public StringType {
public:
///
/// \brief Create a variable length string HDF5 datatype.
///
explicit VariableLengthStringType(CharacterSet character_set = CharacterSet::Ascii);
};
///
/// \brief create an HDF5 DataType from a C++ type
///
/// Support only basic data type
///
template <typename T>
class AtomicType: public DataType {
public:
AtomicType();
using basic_type = T;
};
///
/// \brief Create a compound HDF5 datatype
///
class CompoundType: public DataType {
public:
///
/// \brief Use for defining a sub-type of compound type
struct member_def {
member_def(std::string t_name, DataType t_base_type, size_t t_offset = 0)
: name(std::move(t_name))
, base_type(std::move(t_base_type))
, offset(t_offset) {}
std::string name;
DataType base_type;
size_t offset;
};
///
/// \brief Initializes a compound type from a vector of member definitions
/// \param t_members
/// \param size
inline CompoundType(const std::vector<member_def>& t_members, size_t size = 0)
: members(t_members) {
create(size);
}
inline CompoundType(std::vector<member_def>&& t_members, size_t size = 0)
: members(std::move(t_members)) {
create(size);
}
inline CompoundType(const std::initializer_list<member_def>& t_members, size_t size = 0)
: members(t_members) {
create(size);
}
///
/// \brief Initializes a compound type from a DataType
/// \param type
inline explicit CompoundType(DataType&& type)
: DataType(type) {
if (getClass() != DataTypeClass::Compound) {
std::ostringstream ss;
ss << "hid " << _hid << " does not refer to a compound data type";
throw DataTypeException(ss.str());
}
size_t n_members = static_cast<size_t>(detail::h5t_get_nmembers(_hid));
members.reserve(n_members);
for (unsigned i = 0; i < n_members; i++) {
char* name = detail::h5t_get_member_name(_hid, i);
size_t offset = detail::h5t_get_member_offset(_hid, i);
hid_t member_hid = detail::h5t_get_member_type(_hid, i);
DataType member_type{member_hid};
members.emplace_back(std::string(name), member_type, offset);
detail::h5_free_memory(name);
}
}
/// \brief Commit datatype into the given Object
/// \param object Location to commit object into
/// \param name Name to give the datatype
inline void commit(const Object& object, const std::string& name) const;
/// \brief Get read access to the CompoundType members
inline const std::vector<member_def>& getMembers() const noexcept {
return members;
}
private:
/// A vector of the member_def members of this CompoundType
std::vector<member_def> members;
/// \brief Automatically create the type from the set of members
/// using standard struct alignment.
/// \param size Total size of data type
void create(size_t size = 0);
};
///
/// \brief Create a enum HDF5 datatype
///
/// \code{.cpp}
/// enum class Position {
/// FIRST = 1,
/// SECOND = 2,
/// };
///
/// EnumType<Position> create_enum_position() {
/// return {{"FIRST", Position::FIRST},
/// {"SECOND", Position::SECOND}};
/// }
///
/// // You have to register the type inside HighFive
/// HIGHFIVE_REGISTER_TYPE(Position, create_enum_position)
///
/// void write_first(H5::File& file) {
/// auto dataset = file.createDataSet("/foo", Position::FIRST);
/// }
/// \endcode
template <typename T>
class EnumType: public DataType {
public:
///
/// \brief Use for defining a member of enum type
struct member_def {
member_def(const std::string& t_name, T t_value)
: name(t_name)
, value(std::move(t_value)) {}
std::string name;
T value;
};
EnumType(const EnumType& other) = default;
EnumType(const std::vector<member_def>& t_members)
: members(t_members) {
static_assert(std::is_enum<T>::value, "EnumType<T>::create takes only enum");
if (members.empty()) {
HDF5ErrMapper::ToException<DataTypeException>(
"Could not create an enum without members");
}
create();
}
EnumType(std::initializer_list<member_def> t_members)
: EnumType(std::vector<member_def>(t_members)) {}
/// \brief Commit datatype into the given Object
/// \param object Location to commit object into
/// \param name Name to give the datatype
void commit(const Object& object, const std::string& name) const;
private:
std::vector<member_def> members;
void create();
};
/// \brief Create a DataType instance representing type T
template <typename T>
DataType create_datatype();
/// \brief Create a DataType instance representing type T and perform a sanity check on its size
template <typename T>
DataType create_and_check_datatype();
} // namespace HighFive
/// \brief Macro to extend datatype of HighFive
///
/// This macro has to be called outside of any namespace.
///
/// \code{.cpp}
/// namespace app {
/// enum FooBar { FOO = 1, BAR = 2 };
/// EnumType create_enum_foobar() {
/// return EnumType<FooBar>({{"FOO", FooBar::FOO},
/// {"BAR", FooBar::BAR}});
/// }
/// }
///
/// HIGHFIVE_REGISTER_TYPE(FooBar, ::app::create_enum_foobar)
/// \endcode
#define HIGHFIVE_REGISTER_TYPE(type, function) \
template <> \
inline HighFive::DataType HighFive::create_datatype<type>() { \
return function(); \
}
#include "bits/H5DataType_misc.hpp"

398
include/highfive/H5Easy.hpp Normal file
View File

@ -0,0 +1,398 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
/// \brief
/// Read/dump DataSets or Attribute using a minimalistic syntax.
/// To this end, the functions are templated, and accept:
/// - Any type accepted by HighFive
/// - Eigen objects
/// - xtensor objects
/// - OpenCV objects
#pragma once
#include <string>
#include <vector>
// optionally enable xtensor plug-in and load the library
#ifdef XTENSOR_VERSION_MAJOR
#ifndef H5_USE_XTENSOR
#define H5_USE_XTENSOR
#endif
#endif
#ifdef H5_USE_XTENSOR
#include "xtensor.hpp"
#endif
// optionally enable Eigen plug-in and load the library
#ifdef EIGEN_WORLD_VERSION
#ifndef H5_USE_EIGEN
#define H5_USE_EIGEN
#endif
#endif
#ifdef H5_USE_EIGEN
#include <Eigen/Eigen>
#include "eigen.hpp"
#endif
// optionally enable OpenCV plug-in and load the library
#ifdef CV_MAJOR_VERSION
#ifndef H5_USE_OPENCV
#define H5_USE_OPENCV
#endif
#endif
#ifdef H5_USE_OPENCV
#include <opencv2/opencv.hpp>
#include "experimental/opencv.hpp"
#endif
#include "H5File.hpp"
namespace H5Easy {
using HighFive::AtomicType;
using HighFive::Attribute;
using HighFive::Chunking;
using HighFive::DataSet;
using HighFive::DataSetCreateProps;
using HighFive::DataSpace;
using HighFive::Deflate;
using HighFive::Exception;
using HighFive::File;
using HighFive::ObjectType;
using HighFive::Shuffle;
///
/// \brief Write mode for DataSets
enum class DumpMode {
Create = 0, /*!< Dump only if DataSet does not exist, otherwise throw. */
Overwrite = 1 /*!< Create or overwrite if DataSet of correct shape exists, otherwise throw. */
};
///
/// \brief Signal to enable/disable automatic flushing after write operations.
enum class Flush {
False = 0, /*!< No automatic flushing. */
True = 1 /*!< Automatic flushing. */
};
///
/// \brief Signal to set compression level for written DataSets.
class Compression {
public:
///
/// \brief Enable compression with the highest compression level (9).
/// or disable compression (set compression level to 0).
///
/// \param enable ``true`` to enable with highest compression level
explicit Compression(bool enable = true);
///
/// \brief Set compression level.
///
/// \param level the compression level
template <class T>
Compression(T level);
///
/// \brief Return compression level.
inline unsigned get() const;
private:
unsigned m_compression_level;
};
///
/// \brief Define options for dumping data.
///
/// By default:
/// - DumpMode::Create
/// - Flush::True
/// - Compression: false
/// - ChunkSize: automatic
class DumpOptions {
public:
///
/// \brief Constructor: accept all default settings.
DumpOptions() = default;
///
/// \brief Constructor: overwrite (some of the) defaults.
/// \param args any of DumpMode(), Flush(), Compression() in arbitrary number and order.
template <class... Args>
DumpOptions(Args... args) {
set(args...);
}
///
/// \brief Overwrite H5Easy::DumpMode setting.
/// \param mode: DumpMode.
inline void set(DumpMode mode);
///
/// \brief Overwrite H5Easy::Flush setting.
/// \param mode Flush.
inline void set(Flush mode);
///
/// \brief Overwrite H5Easy::Compression setting.
/// \param level Compression.
inline void set(const Compression& level);
///
/// \brief Overwrite any setting(s).
/// \param arg any of DumpMode(), Flush(), Compression in arbitrary number and order.
/// \param args any of DumpMode(), Flush(), Compression in arbitrary number and order.
template <class T, class... Args>
inline void set(T arg, Args... args);
///
/// \brief Set chunk-size. If the input is rank (size) zero, automatic chunking is enabled.
/// \param shape Chunk size along each dimension.
template <class T>
inline void setChunkSize(const std::vector<T>& shape);
///
/// \brief Set chunk-size. If the input is rank (size) zero, automatic chunking is enabled.
/// \param shape Chunk size along each dimension.
inline void setChunkSize(std::initializer_list<size_t> shape);
///
/// \brief Get overwrite-mode.
/// \return bool
inline bool overwrite() const;
///
/// \brief Get flush-mode.
/// \return bool
inline bool flush() const;
///
/// \brief Get compress-mode.
/// \return bool
inline bool compress() const;
///
/// \brief Get compression level.
/// \return [0..9]
inline unsigned getCompressionLevel() const;
///
/// \brief Get chunking mode: ``true`` is manually set, ``false`` if chunk-size should be
/// computed automatically.
/// \return bool
inline bool isChunked() const;
///
/// \brief Get chunk size. Use DumpOptions::getChunkSize to check if chunk-size should
/// be automatically computed.
inline std::vector<hsize_t> getChunkSize() const;
private:
bool m_overwrite = false;
bool m_flush = true;
unsigned m_compression_level = 0;
std::vector<hsize_t> m_chunk_size = {};
};
///
/// \brief Get the size of an existing DataSet in an open HDF5 file.
///
/// \param file opened file (has to be readable)
/// \param path path of the DataSet
///
/// \return Size of the DataSet
inline size_t getSize(const File& file, const std::string& path);
///
/// \brief Get the shape of an existing DataSet in an readable file.
///
/// \param file opened file (has to be readable)
/// \param path Path of the DataSet
///
/// \return the shape of the DataSet
inline std::vector<size_t> getShape(const File& file, const std::string& path);
///
/// \brief Write object (templated) to a (new) DataSet in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param data the data to write (any supported type)
/// \param mode write mode
///
/// \return The newly created DataSet
///
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
DumpMode mode = DumpMode::Create);
///
/// \brief Write object (templated) to a (new) DataSet in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param data the data to write (any supported type)
/// \param options dump options
///
/// \return The newly created DataSet
///
template <class T>
inline DataSet dump(File& file, const std::string& path, const T& data, const DumpOptions& options);
///
/// \brief Write a scalar to a (new, extendible) DataSet in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param data the data to write (any supported type)
/// \param idx the indices to which to write
///
/// \return The newly created DataSet
///
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::vector<size_t>& idx);
///
/// \brief Write a scalar to a (new, extendable) DataSet in an open HDF5 file.
///
/// \param file open File (has to be writeable)
/// \param path path of the DataSet
/// \param data the data to write (any supported type)
/// \param idx the indices to which to write
///
/// \return The newly created DataSet
///
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::initializer_list<size_t>& idx);
///
/// \brief Write a scalar to a (new, extendible) DataSet in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param data the data to write (any supported type)
/// \param idx the indices to which to write
/// \param options dump options
///
/// \return The newly created DataSet
///
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::vector<size_t>& idx,
const DumpOptions& options);
///
/// \brief Write a scalar to a (new, extendible) DataSet in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param data the data to write (any supported type)
/// \param idx the indices to which to write
/// \param options dump options
///
/// \return The newly created DataSet
///
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::initializer_list<size_t>& idx,
const DumpOptions& options);
///
/// \brief Load entry ``{i, j, ...}`` from a DataSet in an open HDF5 file to a scalar.
///
/// \param file opened file (has to be writeable)
/// \param idx the indices to load
/// \param path path of the DataSet
///
/// \return The read data
///
template <class T>
inline T load(const File& file, const std::string& path, const std::vector<size_t>& idx);
///
/// \brief Load a DataSet in an open HDF5 file to an object (templated).
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
///
/// \return The read data
///
template <class T>
inline T load(const File& file, const std::string& path);
///
/// \brief Write object (templated) to a (new) Attribute in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param key name of the attribute
/// \param data the data to write (any supported type)
/// \param mode write mode
///
/// \return The newly created DataSet
///
template <class T>
inline Attribute dumpAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
DumpMode mode = DumpMode::Create);
///
/// \brief Write object (templated) to a (new) Attribute in an open HDF5 file.
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param key name of the attribute
/// \param data the data to write (any supported type)
/// \param options dump options
///
/// \return The newly created DataSet
///
template <class T>
inline Attribute dumpAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
const DumpOptions& options);
///
/// \brief Load a Attribute in an open HDF5 file to an object (templated).
///
/// \param file opened file (has to be writeable)
/// \param path path of the DataSet
/// \param key name of the attribute
///
/// \return The read data
///
template <class T>
inline T loadAttribute(const File& file, const std::string& path, const std::string& key);
} // namespace H5Easy
#include "h5easy_bits/H5Easy_Eigen.hpp"
#include "h5easy_bits/H5Easy_misc.hpp"
#include "h5easy_bits/H5Easy_public.hpp"
#include "h5easy_bits/H5Easy_scalar.hpp"

View File

@ -0,0 +1,167 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <memory>
#include <stdexcept>
#include <string>
#include <H5Ipublic.h>
namespace HighFive {
///
/// \brief Basic HighFive Exception class
///
///
class Exception: public std::exception {
public:
explicit Exception(const std::string& err_msg)
: _errmsg(err_msg) {}
Exception(const Exception& other) = default;
Exception(Exception&& other) noexcept = default;
Exception& operator=(const Exception& other) = default;
Exception& operator=(Exception&& other) noexcept = default;
~Exception() noexcept override {}
///
/// \brief get the current exception error message
/// \return
///
inline const char* what() const noexcept override {
return _errmsg.c_str();
}
///
/// \brief define the error message
/// \param errmsg
///
inline virtual void setErrorMsg(const std::string& errmsg) {
_errmsg = errmsg;
}
///
/// \brief nextException
/// \return pointer to the next exception in the chain, or NULL if not
/// existing
///
inline Exception* nextException() const {
return _next.get();
}
///
/// \brief HDF5 library error mapper
/// \return HDF5 major error number
///
inline hid_t getErrMajor() const {
return _err_major;
}
///
/// \brief HDF5 library error mapper
/// \return HDF5 minor error number
///
inline hid_t getErrMinor() const {
return _err_minor;
}
private:
std::string _errmsg;
std::shared_ptr<Exception> _next = nullptr;
hid_t _err_major = 0, _err_minor = 0;
friend struct HDF5ErrMapper;
};
///
/// \brief Exception specific to HighFive Object interface
///
class ObjectException: public Exception {
public:
explicit ObjectException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive DataType interface
///
class DataTypeException: public Exception {
public:
explicit DataTypeException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive File interface
///
class FileException: public Exception {
public:
explicit FileException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive DataSpace interface
///
class DataSpaceException: public Exception {
public:
explicit DataSpaceException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive Attribute interface
///
class AttributeException: public Exception {
public:
explicit AttributeException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive DataSet interface
///
class DataSetException: public Exception {
public:
explicit DataSetException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive Group interface
///
class GroupException: public Exception {
public:
explicit GroupException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive Property interface
///
class PropertyException: public Exception {
public:
explicit PropertyException(const std::string& err_msg)
: Exception(err_msg) {}
};
///
/// \brief Exception specific to HighFive Reference interface
///
class ReferenceException: public Exception {
public:
explicit ReferenceException(const std::string& err_msg)
: Exception(err_msg) {}
};
} // namespace HighFive
#include "bits/H5Exception_misc.hpp"

203
include/highfive/H5File.hpp Normal file
View File

@ -0,0 +1,203 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include <type_traits>
#include "H5Object.hpp"
#include "H5PropertyList.hpp"
#include "bits/H5Annotate_traits.hpp"
#include "bits/H5Node_traits.hpp"
namespace HighFive {
///
/// \brief File class
///
class File: public Object, public NodeTraits<File>, public AnnotateTraits<File> {
public:
const static ObjectType type = ObjectType::File;
enum class AccessMode {
None = 0x00u,
/// Open flag: Read only access
ReadOnly = 0x01u,
/// Open flag: Read Write access
ReadWrite = 0x02u,
/// Open flag: Truncate a file if already existing
Truncate = 0x04u,
/// Open flag: Open will fail if file already exist
Excl = 0x08u,
/// Open flag: Open in debug mode
Debug = 0x10u,
/// Open flag: Create non existing file
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
/// \param openFlags: Open mode / flags ( ReadOnly, ReadWrite)
/// \param fileAccessProps: the file access properties
///
/// Open or create a new HDF5 file
explicit File(const std::string& filename,
AccessMode openFlags = ReadOnly,
const FileAccessProps& fileAccessProps = FileAccessProps::Default());
///
/// \brief File
/// \param filename: filepath of the HDF5 file
/// \param access_mode: Open mode / flags ( ReadOnly, ReadWrite, etc.)
/// \param fileCreateProps: the file create properties
/// \param fileAccessProps: the file access properties
///
/// Open or create a new HDF5 file
File(const std::string& filename,
AccessMode access_mode,
const FileCreateProps& fileCreateProps,
const FileAccessProps& fileAccessProps = FileAccessProps::Default());
/// \brief Keeps reference count constant, and invalidates other.
File(File&& other) noexcept = default;
/// \brief Keeps reference count constant, and invalidates other.
File& operator=(File&& other) = default;
/// \brief Increments reference count, keeps other valid.
File(const File& other) = default;
/// \brief Increments reference count, keeps other valid.
File& operator=(const File& other) = default;
///
/// \brief Return the name of the file
///
const std::string& getName() const;
/// \brief Object path of a File is always "/"
std::string getPath() const noexcept {
return "/";
}
/// \brief Returns the block size for metadata in bytes
hsize_t getMetadataBlockSize() const;
/// \brief Returns the HDF5 version compatibility bounds
std::pair<H5F_libver_t, H5F_libver_t> getVersionBounds() const;
#if H5_VERSION_GE(1, 10, 1)
/// \brief Returns the HDF5 file space strategy.
H5F_fspace_strategy_t getFileSpaceStrategy() const;
/// \brief Returns the page size, if paged allocation is used.
hsize_t getFileSpacePageSize() const;
#endif
///
/// \brief flush
///
/// Flushes all buffers associated with a file to disk
///
void flush();
/// \brief Get the list of properties for creation of this file
FileCreateProps getCreatePropertyList() const {
return details::get_plist<FileCreateProps>(*this, H5Fget_create_plist);
}
/// \brief Get the list of properties for accession of this file
FileAccessProps getAccessPropertyList() const {
return details::get_plist<FileAccessProps>(*this, H5Fget_access_plist);
}
/// \brief Get the size of this file in bytes
size_t getFileSize() const;
/// \brief Get the amount of tracked, unused space in bytes.
///
/// Note, this is a wrapper for `H5Fget_freespace` and returns the number
/// bytes in the free space manager. This might be different from the total
/// amount of unused space in the HDF5 file, since the free space manager
/// might not track everything or not track across open-close cycles.
size_t getFreeSpace() const;
protected:
File() = default;
using Object::Object;
private:
mutable std::string _filename{};
template <typename>
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
#include "bits/H5Annotate_traits_misc.hpp"
#include "bits/H5File_misc.hpp"
#include "bits/H5Node_traits_misc.hpp"
#include "bits/H5Path_traits_misc.hpp"

View File

@ -0,0 +1,86 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <H5Gpublic.h>
#include "H5Object.hpp"
#include "bits/H5Friends.hpp"
#include "bits/H5_definitions.hpp"
#include "bits/H5Annotate_traits.hpp"
#include "bits/H5Node_traits.hpp"
#include "bits/H5Path_traits.hpp"
namespace HighFive {
namespace detail {
/// \brief Internal hack to create an `Group` from an ID.
///
/// WARNING: Creating an Group from an ID has implications w.r.t. the lifetime of the object
/// that got passed via its ID. Using this method careless opens up the suite of issues
/// related to C-style resource management, including the analog of double free, dangling
/// pointers, etc.
///
/// NOTE: This is not part of the API and only serves to work around a compiler issue in GCC which
/// prevents us from using `friend`s instead. This function should only be used for internal
/// purposes. The problematic construct is:
///
/// template<class Derived>
/// friend class SomeCRTP<Derived>;
///
/// \private
Group make_group(hid_t);
} // namespace detail
///
/// \brief Represents an hdf5 group
class Group: public Object,
public NodeTraits<Group>,
public AnnotateTraits<Group>,
public PathTraits<Group> {
public:
const static ObjectType type = ObjectType::Group;
Group() = default;
std::pair<unsigned int, unsigned int> getEstimatedLinkInfo() const;
/// \brief Get the list of properties for creation of this group
GroupCreateProps getCreatePropertyList() const {
return details::get_plist<GroupCreateProps>(*this, H5Gget_create_plist);
}
explicit Group(Object&& o) noexcept
: Object(std::move(o)) {};
protected:
using Object::Object;
friend Group detail::make_group(hid_t);
friend class File;
friend class Reference;
#if HIGHFIVE_HAS_FRIEND_DECLARATIONS
template <typename Derivate>
friend class ::HighFive::NodeTraits;
#endif
};
inline std::pair<unsigned int, unsigned int> Group::getEstimatedLinkInfo() const {
auto gcpl = getCreatePropertyList();
auto eli = EstimatedLinkInfo(gcpl);
return std::make_pair(eli.getEntries(), eli.getNameLength());
}
namespace detail {
inline Group make_group(hid_t hid) {
return Group(hid);
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,139 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <ctime>
#include "bits/H5_definitions.hpp"
#include "bits/H5Friends.hpp"
#include "H5Exception.hpp"
#include "bits/h5o_wrapper.hpp"
#include "bits/h5i_wrapper.hpp"
namespace HighFive {
///
/// \brief Enum of the types of objects (H5O api)
///
enum class ObjectType {
File,
Group,
UserDataType,
DataSpace,
Dataset,
Attribute,
Other // Internal/custom object type
};
class Object {
public:
// move constructor, reuse hid
Object(Object&& other) noexcept;
///
/// \brief isValid
/// \return true if current Object is a valid HDF5Object
///
bool isValid() const noexcept;
///
/// \brief getId
/// \return internal HDF5 id to the object
/// provided for C API compatibility
///
hid_t getId() const noexcept;
///
/// \brief Retrieve several infos about the current object (address, dates, etc)
///
ObjectInfo getInfo() const;
///
/// \brief Address of an HDF5 object in the file.
///
/// Not all HDF5 files support addresses anymore. The more recent concept
/// is a VOL token.
///
/// \since 3.0.0
///
haddr_t getAddress() const;
///
/// \brief Gets the fundamental type of the object (dataset, group, etc)
/// \exception ObjectException when the _hid is negative or the type
/// is custom and not registered yet
///
ObjectType getType() const;
// Check if refer to same object
bool operator==(const Object& other) const noexcept {
return _hid == other._hid;
}
protected:
// empty constructor
Object();
// copy constructor, increase reference counter
Object(const Object& other);
// Init with an low-level object id
explicit Object(hid_t) noexcept;
// decrease reference counter
~Object();
// Copy-Assignment operator
Object& operator=(const Object& other);
Object& operator=(Object&& other);
hid_t _hid;
private:
friend class Reference;
friend class CompoundType;
#if HIGHFIVE_HAS_FRIEND_DECLARATIONS
template <typename Derivate>
friend class NodeTraits;
template <typename Derivate>
friend class AnnotateTraits;
template <typename Derivate>
friend class PathTraits;
#endif
};
///
/// \brief A class for accessing hdf5 objects info
///
class ObjectInfo {
public:
ObjectInfo(const Object& obj);
/// \brief Retrieve the number of references to this object
size_t getRefCount() const noexcept;
/// \brief Retrieve the object's creation time
time_t getCreationTime() const noexcept;
/// \brief Retrieve the object's last modification time
time_t getModificationTime() const noexcept;
private:
detail::h5o_info1_t raw_info;
friend class Object;
};
} // namespace HighFive
#include "bits/H5Object_misc.hpp"

View File

@ -0,0 +1,710 @@
/*
* Copyright (c), 2017-2018, Adrien Devresse <adrien.devresse@epfl.ch>
* Juan Hernando <juan.hernando@epfl.ch>
* 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)
*
*/
#pragma once
#include <vector>
#include <H5Ppublic.h>
// Required by MPIOFileAccess
#ifdef H5_HAVE_PARALLEL
#include <H5FDmpi.h>
#endif
#include "H5Exception.hpp"
#include "H5Object.hpp"
namespace HighFive {
/// \defgroup PropertyLists Property Lists
/// HDF5 is configured through what they call property lists. In HDF5 the
/// process has four steps:
///
/// 1. Create a property list. As users we now have an `hid_t` identifying the
/// property list.
/// 2. Set properties as desired.
/// 3. Pass the HID to the HDF5 function to be configured.
/// 4. Free the property list.
///
/// Note that the mental picture is that one creates a settings object, and
/// then passes those settings to a function such as `H5Dwrite`. In and of
/// themselves the settings don't change the behaviour of HDF5. Rather they
/// need to be used to take affect.
///
/// The second aspect is that property lists represent any number of related
/// settings, e.g. there's property lists anything related to creating files
/// and another for accessing files, same for creating and accessing datasets,
/// etc. Settings that affect creating files, must be passed a file creation
/// property list, while settings that affect file access require a file access
/// property list.
///
/// In HighFive the `PropertyList` works similar in that it's a object
/// representing the settings, i.e. internally it's just the property lists
/// HID. Just like in HDF5 one adds the settings to the settings object; and
/// then passes the settings object to the respective method. Example:
///
///
/// // Create an object which contains the setting to
/// // open files with MPI-IO.
/// auto fapl = FileAccessProps();
/// fapl.add(MPIOFileAccess(MPI_COMM_WORLD, MPI_INFO_NULL);
///
/// // To open a specific file with MPI-IO, we do:
/// auto file = File("foo.h5", File::ReadOnly, fapl);
///
/// Note that the `MPIOFileAccess` object by itself doesn't affect the
/// `FileAccessProps`. Rather it needs to be explicitly added to the `fapl`
/// (the group of file access related settings), and then the `fapl` needs to
/// be passed to the constructor of `File` for the settings to take affect.
///
/// This is important to understand when reading properties. Example:
///
/// // Obtain the file access property list:
/// auto fapl = file.getAccessPropertyList()
///
/// // Extracts a copy of the collective MPI-IO metadata settings from
/// // the group of file access related setting, i.e. the `fapl`:
/// auto mpio_metadata = MPIOCollectiveMetadata(fapl);
///
/// if(mpio_metadata.isCollectiveRead()) {
/// // something specific if meta data is read collectively.
/// }
///
/// // Careful, this only affects the `mpio_metadata` object, but not the
/// // `fapl`, and also not whether `file` uses collective MPI-IO for
/// // metadata.
/// mpio_metadata = MPIOCollectiveMetadata(false, false);
///
/// @{
///
/// \brief Types of property lists
///
enum class PropertyType : int {
FILE_CREATE,
FILE_ACCESS,
DATASET_CREATE,
DATASET_ACCESS,
DATASET_XFER,
GROUP_CREATE,
GROUP_ACCESS,
DATATYPE_CREATE,
DATATYPE_ACCESS,
STRING_CREATE,
ATTRIBUTE_CREATE,
LINK_CREATE,
LINK_ACCESS,
};
namespace details {
template <typename T, typename U>
T get_plist(const U& obj, hid_t (*f)(hid_t)) {
auto hid = f(obj.getId());
if (hid < 0) {
HDF5ErrMapper::ToException<PropertyException>("Unable to get property list");
}
T t{};
t._hid = hid;
return t;
}
} // namespace details
///
/// \brief Base Class for Property lists, providing global default
class PropertyListBase: public Object {
public:
PropertyListBase() noexcept;
static const PropertyListBase& Default() noexcept {
static const PropertyListBase plist{};
return plist;
}
private:
template <typename T, typename U>
friend T details::get_plist(const U&, hid_t (*f)(hid_t));
};
///
/// \brief HDF5 property Lists
///
template <PropertyType T>
class PropertyList: public PropertyListBase {
public:
///
/// \brief return the type of this PropertyList
constexpr PropertyType getType() const noexcept {
return T;
}
///
/// Add a property to this property list.
/// A property is an object which is expected to have a method with the
/// following signature void apply(hid_t hid) const
template <typename P>
void add(const P& property);
///
/// Return the Default property type object
static const PropertyList<T>& Default() noexcept {
return static_cast<const PropertyList<T>&>(PropertyListBase::Default());
}
/// Return a property list created via a call to `H5Pcreate`.
///
/// An empty property is needed when one wants `getId()` to immediately
/// point at a valid HID. This is important when interfacing directly with
/// HDF5 to set properties that haven't been wrapped by HighFive.
static PropertyList<T> Empty() {
auto plist = PropertyList<T>();
plist._initializeIfNeeded();
return plist;
}
protected:
void _initializeIfNeeded();
};
using FileCreateProps = PropertyList<PropertyType::FILE_CREATE>;
using FileAccessProps = PropertyList<PropertyType::FILE_ACCESS>;
using DataSetCreateProps = PropertyList<PropertyType::DATASET_CREATE>;
using DataSetAccessProps = PropertyList<PropertyType::DATASET_ACCESS>;
using DataTransferProps = PropertyList<PropertyType::DATASET_XFER>;
using GroupCreateProps = PropertyList<PropertyType::GROUP_CREATE>;
using GroupAccessProps = PropertyList<PropertyType::GROUP_ACCESS>;
using DataTypeCreateProps = PropertyList<PropertyType::DATATYPE_CREATE>;
using DataTypeAccessProps = PropertyList<PropertyType::DATATYPE_ACCESS>;
using StringCreateProps = PropertyList<PropertyType::STRING_CREATE>;
using AttributeCreateProps = PropertyList<PropertyType::ATTRIBUTE_CREATE>;
using LinkCreateProps = PropertyList<PropertyType::LINK_CREATE>;
using LinkAccessProps = PropertyList<PropertyType::LINK_ACCESS>;
///
/// RawPropertyLists are to be used when advanced H5 properties
/// are desired and are not part of the HighFive API.
/// Therefore this class is mainly for internal use.
template <PropertyType T>
class RawPropertyList: public PropertyList<T> {
public:
template <typename F, typename... Args>
void add(const F& funct, const Args&... args);
};
#ifdef H5_HAVE_PARALLEL
///
/// \brief Configure MPI access for the file
///
/// All further modifications to the structure of the file will have to be
/// done with collective operations
///
class MPIOFileAccess {
public:
MPIOFileAccess(MPI_Comm comm, MPI_Info info);
private:
friend FileAccessProps;
void apply(hid_t list) const;
MPI_Comm _comm;
MPI_Info _info;
};
#if H5_VERSION_GE(1, 10, 0)
///
/// \brief Use collective MPI-IO for metadata read and write.
///
/// See `MPIOCollectiveMetadataRead` and `MPIOCollectiveMetadataWrite`.
///
class MPIOCollectiveMetadata {
public:
explicit MPIOCollectiveMetadata(bool collective = true);
explicit MPIOCollectiveMetadata(const FileAccessProps& plist);
bool isCollectiveRead() const;
bool isCollectiveWrite() const;
private:
friend FileAccessProps;
void apply(hid_t plist) const;
bool collective_read_;
bool collective_write_;
};
///
/// \brief Use collective MPI-IO for metadata read?
///
/// Note that when used in a file access property list, this will force all reads
/// of meta data to be collective. HDF5 function may implicitly perform metadata
/// reads. These functions would become collective. A list of functions that
/// perform metadata reads can be found in the HDF5 documentation, e.g.
/// https://docs.hdfgroup.org/hdf5/v1_12/group___g_a_c_p_l.html
///
/// In HighFive setting collective read is (currently) only supported on file level.
///
/// Please also consult upstream documentation of `H5Pset_all_coll_metadata_ops`.
///
class MPIOCollectiveMetadataRead {
public:
explicit MPIOCollectiveMetadataRead(bool collective = true);
explicit MPIOCollectiveMetadataRead(const FileAccessProps& plist);
bool isCollective() const;
private:
friend FileAccessProps;
friend MPIOCollectiveMetadata;
void apply(hid_t plist) const;
bool collective_;
};
///
/// \brief Use collective MPI-IO for metadata write?
///
/// In order to keep the in-memory representation of the file structure
/// consistent across MPI ranks, writing meta data is always a collective
/// operation. Meaning all MPI ranks must participate. Passing this setting
/// enables using MPI-IO collective operations for metadata writes.
///
/// Please also consult upstream documentation of `H5Pset_coll_metadata_write`.
///
class MPIOCollectiveMetadataWrite {
public:
explicit MPIOCollectiveMetadataWrite(bool collective = true);
explicit MPIOCollectiveMetadataWrite(const FileAccessProps& plist);
bool isCollective() const;
private:
friend FileAccessProps;
friend MPIOCollectiveMetadata;
void apply(hid_t plist) const;
bool collective_;
};
#endif
#endif
///
/// \brief Configure the version bounds for the file
///
/// Used to define the compatibility of objects created within HDF5 files,
/// and affects the format of groups stored in the file.
///
/// See also the documentation of \c H5P_SET_LIBVER_BOUNDS in HDF5.
///
/// Possible values for \c low and \c high are:
/// * \c H5F_LIBVER_EARLIEST
/// * \c H5F_LIBVER_V18
/// * \c H5F_LIBVER_V110
/// * \c H5F_LIBVER_NBOUNDS
/// * \c H5F_LIBVER_LATEST currently defined as \c H5F_LIBVER_V110 within
/// HDF5
///
class FileVersionBounds {
public:
FileVersionBounds(H5F_libver_t low, H5F_libver_t high);
explicit FileVersionBounds(const FileAccessProps& fapl);
std::pair<H5F_libver_t, H5F_libver_t> getVersion() const;
private:
friend FileAccessProps;
void apply(hid_t list) const;
H5F_libver_t _low;
H5F_libver_t _high;
};
///
/// \brief Configure the metadata block size to use writing to files
///
/// \param size Metadata block size in bytes
///
class MetadataBlockSize {
public:
explicit MetadataBlockSize(hsize_t size);
explicit MetadataBlockSize(const FileAccessProps& fapl);
hsize_t getSize() const;
private:
friend FileAccessProps;
void apply(hid_t list) const;
hsize_t _size;
};
#if H5_VERSION_GE(1, 10, 1)
///
/// \brief Configure the file space strategy.
///
/// See the upstream documentation of `H5Pget_file_space_strategy` for more details. Essentially,
/// it enables configuring how space is allocate in the file.
///
class FileSpaceStrategy {
public:
///
/// \brief Create a file space strategy property.
///
/// \param strategy The HDF5 free space strategy.
/// \param persist Should free space managers be persisted across file closing and reopening.
/// \param threshold The free-space manager wont track sections small than this threshold.
FileSpaceStrategy(H5F_fspace_strategy_t strategy, hbool_t persist, hsize_t threshold);
explicit FileSpaceStrategy(const FileCreateProps& fcpl);
H5F_fspace_strategy_t getStrategy() const;
hbool_t getPersist() const;
hsize_t getThreshold() const;
private:
friend FileCreateProps;
void apply(hid_t list) const;
H5F_fspace_strategy_t _strategy;
hbool_t _persist;
hsize_t _threshold;
};
///
/// \brief Configure the page size for paged allocation.
///
/// See the upstream documentation of `H5Pset_file_space_page_size` for more details. Essentially,
/// it enables configuring the page size when paged allocation is used.
///
/// General information about paged allocation can be found in the upstream documentation "RFC: Page
/// Buffering".
///
class FileSpacePageSize {
public:
///
/// \brief Create a file space strategy property.
///
/// \param page_size The page size in bytes.
explicit FileSpacePageSize(hsize_t page_size);
explicit FileSpacePageSize(const FileCreateProps& fcpl);
hsize_t getPageSize() const;
private:
friend FileCreateProps;
void apply(hid_t list) const;
hsize_t _page_size;
};
#ifndef H5_HAVE_PARALLEL
/// \brief Set size of the page buffer.
///
/// Please, consult the upstream documentation of
/// H5Pset_page_buffer_size
/// H5Pget_page_buffer_size
/// Note that this setting is only valid for page allocated/aggregated
/// files, i.e. those that have file space strategy "Page".
///
/// Tests suggest this doesn't work in the parallel version of the
/// library. Hence, this isn't available at compile time if the parallel
/// library was selected.
class PageBufferSize {
public:
/// Property to set page buffer sizes.
///
/// @param page_buffer_size maximum size of the page buffer in bytes.
/// @param min_meta_percent fraction of the page buffer dedicated to meta data, in percent.
/// @param min_raw_percent fraction of the page buffer dedicated to raw data, in percent.
explicit PageBufferSize(size_t page_buffer_size,
unsigned min_meta_percent = 0,
unsigned min_raw_percent = 0);
explicit PageBufferSize(const FileAccessProps& fapl);
size_t getPageBufferSize() const;
unsigned getMinMetaPercent() const;
unsigned getMinRawPercent() const;
private:
friend FileAccessProps;
void apply(hid_t list) const;
size_t _page_buffer_size;
unsigned _min_meta;
unsigned _min_raw;
};
#endif
#endif
/// \brief Set hints as to how many links to expect and their average length
///
class EstimatedLinkInfo {
public:
/// \brief Create a property with the request parameters.
///
/// @param entries The estimated number of links in a group.
/// @param length The estimated length of the names of links.
explicit EstimatedLinkInfo(unsigned entries, unsigned length);
explicit EstimatedLinkInfo(const GroupCreateProps& gcpl);
/// \brief The estimated number of links in a group.
unsigned getEntries() const;
/// \brief The estimated length of the names of links.
unsigned getNameLength() const;
private:
friend GroupCreateProps;
void apply(hid_t hid) const;
unsigned _entries;
unsigned _length;
};
class Chunking {
public:
explicit Chunking(const std::vector<hsize_t>& dims);
Chunking(const std::initializer_list<hsize_t>& items);
template <typename... Args>
explicit Chunking(hsize_t item, Args... args);
explicit Chunking(DataSetCreateProps& plist, size_t max_dims = 32);
const std::vector<hsize_t>& getDimensions() const;
private:
friend DataSetCreateProps;
void apply(hid_t hid) const;
std::vector<hsize_t> _dims;
};
class Deflate {
public:
explicit Deflate(unsigned level);
private:
friend DataSetCreateProps;
friend GroupCreateProps;
void apply(hid_t hid) const;
const unsigned _level;
};
class Szip {
public:
explicit Szip(unsigned options_mask = H5_SZIP_EC_OPTION_MASK,
unsigned pixels_per_block = H5_SZIP_MAX_PIXELS_PER_BLOCK);
unsigned getOptionsMask() const;
unsigned getPixelsPerBlock() const;
private:
friend DataSetCreateProps;
void apply(hid_t hid) const;
const unsigned _options_mask;
const unsigned _pixels_per_block;
};
class Shuffle {
public:
Shuffle() = default;
private:
friend DataSetCreateProps;
void apply(hid_t hid) const;
};
/// \brief When are datasets allocated?
///
/// The precise time of when HDF5 requests space to store the dataset
/// can be configured. Please, consider the upstream documentation for
/// `H5Pset_alloc_time`.
class AllocationTime {
public:
explicit AllocationTime(H5D_alloc_time_t alloc_time);
explicit AllocationTime(const DataSetCreateProps& dcpl);
H5D_alloc_time_t getAllocationTime();
private:
friend DataSetCreateProps;
void apply(hid_t dcpl) const;
H5D_alloc_time_t _alloc_time;
};
/// Dataset access property to control chunk cache configuration.
/// Do not confuse with the similar file access property for H5Pset_cache
class Caching {
public:
/// https://support.hdfgroup.org/HDF5/doc/RM/H5P/H5Pset_chunk_cache.html for
/// details.
Caching(size_t numSlots,
size_t cacheSize,
double w0 = static_cast<double>(H5D_CHUNK_CACHE_W0_DEFAULT));
explicit Caching(const DataSetCreateProps& dcpl);
size_t getNumSlots() const;
size_t getCacheSize() const;
double getW0() const;
private:
friend DataSetAccessProps;
void apply(hid_t hid) const;
size_t _numSlots;
size_t _cacheSize;
double _w0;
};
class CreateIntermediateGroup {
public:
explicit CreateIntermediateGroup(bool create = true);
explicit CreateIntermediateGroup(const LinkCreateProps& lcpl);
bool isSet() const;
protected:
void fromPropertyList(hid_t hid);
private:
friend LinkCreateProps;
void apply(hid_t hid) const;
bool _create;
};
#ifdef H5_HAVE_PARALLEL
class UseCollectiveIO {
public:
explicit UseCollectiveIO(bool enable = true);
explicit UseCollectiveIO(const DataTransferProps& dxpl);
/// \brief Does the property request collective IO?
bool isCollective() const;
private:
friend DataTransferProps;
void apply(hid_t hid) const;
bool _enable;
};
/// \brief The cause for non-collective I/O.
///
/// The cause refers to the most recent I/O with data transfer property list `dxpl` at time of
/// creation of this object. This object will not update automatically for later data transfers,
/// i.e. `H5Pget_mpio_no_collective_cause` is called in the constructor, and not when fetching
/// a value, such as `wasCollective`.
class MpioNoCollectiveCause {
public:
explicit MpioNoCollectiveCause(const DataTransferProps& dxpl);
/// \brief Was the datatransfer collective?
bool wasCollective() const;
/// \brief The local cause for a non-collective I/O.
uint32_t getLocalCause() const;
/// \brief The global cause for a non-collective I/O.
uint32_t getGlobalCause() const;
/// \brief A pair of the local and global cause for non-collective I/O.
std::pair<uint32_t, uint32_t> getCause() const;
private:
friend DataTransferProps;
uint32_t _local_cause;
uint32_t _global_cause;
};
#endif
struct CreationOrder {
enum _creation_order {
Tracked = H5P_CRT_ORDER_TRACKED,
Indexed = H5P_CRT_ORDER_INDEXED,
};
};
///
/// \brief Track and index creation order time
///
/// Let user retrieve objects by creation order time instead of name.
///
class LinkCreationOrder {
public:
///
/// \brief Create the property
/// \param flags Should be a composition of HighFive::CreationOrder.
///
explicit LinkCreationOrder(unsigned flags)
: _flags(flags) {}
explicit LinkCreationOrder(const FileCreateProps& fcpl);
explicit LinkCreationOrder(const GroupCreateProps& gcpl);
unsigned getFlags() const;
protected:
void fromPropertyList(hid_t hid);
private:
friend FileCreateProps;
friend GroupCreateProps;
void apply(hid_t hid) const;
unsigned _flags;
};
///
/// \brief Set threshold for attribute storage.
///
/// HDF5 can store Attributes in the object header (compact) or in the B-tree
/// (dense). This property sets the threshold when attributes are moved to one
/// or the other storage format.
///
/// Please refer to the upstream documentation of `H5Pset_attr_phase_change` or
/// Section 8 (Attributes) in the User Guide, in particular Subsection 8.5.
///
class AttributePhaseChange {
public:
///
/// \brief Create the property from the threshold values.
///
/// When the number of attributes hits `max_compact` the attributes are
/// moved to dense storage, once the number drops to below `min_dense` the
/// attributes are moved to compact storage.
AttributePhaseChange(unsigned max_compact, unsigned min_dense);
/// \brief Extract threshold values from property list.
explicit AttributePhaseChange(const GroupCreateProps& gcpl);
unsigned max_compact() const;
unsigned min_dense() const;
private:
friend GroupCreateProps;
void apply(hid_t hid) const;
unsigned _max_compact;
unsigned _min_dense;
};
/// @}
} // namespace HighFive
#include "bits/H5PropertyList_misc.hpp"

View File

@ -0,0 +1,81 @@
/*
* Copyright (c), 2020, EPFL - Blue Brain Project
*
* 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)
*
*/
#pragma once
#include <string>
#include <vector>
#include <H5Ipublic.h>
#include <H5Rpublic.h>
#include "bits/H5_definitions.hpp"
namespace HighFive {
namespace details {
template <typename T>
struct inspector;
}
///
/// \brief An HDF5 (object) reference type
///
/// HDF5 object references allow pointing to groups, datasets (and compound types). They
/// differ from links in their ability to be stored and retrieved as data from the HDF5
/// file in datasets themselves.
///
class Reference {
public:
/// \brief Create an empty Reference to be initialized later
Reference() = default;
/// \brief Create a Reference to an object residing at a given location
///
/// \param location A File or Group where the object being referenced to resides
/// \param object A Dataset or Group to be referenced
Reference(const Object& location, const Object& object);
/// \brief Retrieve the Object being referenced by the Reference
///
/// \tparam T the appropriate HighFive Container (either DataSet or Group)
/// \param location the location where the referenced object is to be found (a File)
/// \return the dereferenced Object (either a Group or DataSet)
template <typename T>
T dereference(const Object& location) const;
/// \brief Get only the type of the referenced Object
///
/// \param location the location where the referenced object is to be found (a File)
/// \return the ObjectType of the referenced object
ObjectType getType(const Object& location) const;
protected:
/// \brief Create a Reference from a low-level HDF5 object reference
inline explicit Reference(const hobj_ref_t h5_ref)
: href(h5_ref) {};
/// \brief Create the low-level reference and store it at refptr
///
/// \param refptr Pointer to a memory location where the created HDF5 reference will
/// be stored
void create_ref(hobj_ref_t* refptr) const;
private:
Object get_ref(const Object& location) const;
hobj_ref_t href{};
std::string obj_name{};
hid_t parent_id{};
friend struct details::inspector<Reference>;
};
} // namespace HighFive
#include "bits/H5Reference_misc.hpp"

View File

@ -0,0 +1,68 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include "H5DataSet.hpp"
#include "H5DataSpace.hpp"
#include "bits/H5Slice_traits.hpp"
#include "bits/H5Friends.hpp"
namespace HighFive {
namespace detail {
Selection make_selection(const DataSpace&, const DataSpace&, const DataSet&);
}
///
/// \brief Selection: represent a view on a slice/part of a dataset
///
/// A Selection is valid only if its parent dataset is valid
///
class Selection: public SliceTraits<Selection> {
public:
///
/// \brief getSpace
/// \return Dataspace associated with this selection
///
DataSpace getSpace() const;
///
/// \brief getMemSpace
/// \return Dataspace associated with the memory representation of this
/// selection
///
DataSpace getMemSpace() const;
///
/// \brief getDataSet
/// \return parent dataset of this selection
///
DataSet& getDataset();
const DataSet& getDataset() const;
///
/// \brief return the datatype of the selection
/// \return return the datatype of the selection
DataType getDataType() const;
protected:
Selection(const DataSpace& memspace, const DataSpace& file_space, const DataSet& set);
private:
DataSpace _mem_space, _file_space;
DataSet _set;
#if HIGHFIVE_HAS_FRIEND_DECLARATIONS
template <typename Derivate>
friend class ::HighFive::SliceTraits;
#endif
friend Selection detail::make_selection(const DataSpace&, const DataSpace&, const DataSet&);
};
} // namespace HighFive

View File

@ -0,0 +1,218 @@
/*
* Copyright (c), 2017, Blue Brain Project - EPFL (CH)
*
* 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)
*
*/
#pragma once
#include <functional>
#include <string>
#include <iostream>
#include "bits/h5e_wrapper.hpp"
#include "bits/H5Friends.hpp"
namespace HighFive {
///
/// \brief Utility class to disable HDF5 stack printing inside a scope.
///
class SilenceHDF5 {
public:
inline explicit SilenceHDF5(bool enable = true)
: _client_data(nullptr) {
detail::nothrow::h5e_get_auto2(H5E_DEFAULT, &_func, &_client_data);
if (enable) {
detail::nothrow::h5e_set_auto2(H5E_DEFAULT, nullptr, nullptr);
}
}
inline ~SilenceHDF5() {
detail::nothrow::h5e_set_auto2(H5E_DEFAULT, _func, _client_data);
}
private:
H5E_auto2_t _func;
void* _client_data;
};
#define HIGHFIVE_LOG_LEVEL_DEBUG 10
#define HIGHFIVE_LOG_LEVEL_INFO 20
#define HIGHFIVE_LOG_LEVEL_WARN 30
#define HIGHFIVE_LOG_LEVEL_ERROR 40
#ifndef HIGHFIVE_LOG_LEVEL
#define HIGHFIVE_LOG_LEVEL HIGHFIVE_LOG_LEVEL_WARN
#endif
enum class LogSeverity {
Debug = HIGHFIVE_LOG_LEVEL_DEBUG,
Info = HIGHFIVE_LOG_LEVEL_INFO,
Warn = HIGHFIVE_LOG_LEVEL_WARN,
Error = HIGHFIVE_LOG_LEVEL_ERROR
};
inline std::string to_string(LogSeverity severity) {
switch (severity) {
case LogSeverity::Debug:
return "DEBUG";
case LogSeverity::Info:
return "INFO";
case LogSeverity::Warn:
return "WARN";
case LogSeverity::Error:
return "ERROR";
default:
return "??";
}
}
/** \brief A logger with supporting basic functionality.
*
* This logger delegates the logging task to a callback. This level of
* indirection enables using the default Python logger from C++; or
* integrating HighFive into some custom logging solution.
*
* Using this class directly to log is not intended. Rather you should use
* - `HIGHFIVE_LOG_DEBUG{,_IF}`
* - `HIGHFIVE_LOG_INFO{,_IF}`
* - `HIGHFIVE_LOG_WARNING{,_IF}`
* - `HIGHFIVE_LOG_ERROR{,_IF}`
*
* This is intended to used as a singleton, via `get_global_logger()`.
*/
class Logger {
public:
using callback_type =
std::function<void(LogSeverity, const std::string&, const std::string&, int)>;
public:
Logger() = delete;
Logger(const Logger&) = delete;
Logger(Logger&&) = delete;
explicit Logger(callback_type cb)
: _cb(std::move(cb)) {}
Logger& operator=(const Logger&) = delete;
Logger& operator=(Logger&&) = delete;
inline void log(LogSeverity severity,
const std::string& message,
const std::string& file,
int line) {
_cb(severity, message, file, line);
}
inline void set_logging_callback(callback_type cb) {
_cb = std::move(cb);
}
private:
callback_type _cb;
};
inline void default_logging_callback(LogSeverity severity,
const std::string& message,
const std::string& file,
int line) {
std::clog << file << ": " << line << " [" << to_string(severity) << "] " << message
<< std::endl;
}
/// \brief Obtain a reference to the logger used by HighFive.
///
/// This uses a Meyers singleton, to ensure that the global logger is
/// initialized with a safe default logger, before it is used.
///
/// Note: You probably don't need to call this function explicitly.
///
inline Logger& get_global_logger() {
static Logger logger(&default_logging_callback);
return logger;
}
/// \brief Sets the callback that's used by the logger.
inline void register_logging_callback(Logger::callback_type cb) {
auto& logger = get_global_logger();
logger.set_logging_callback(std::move(cb));
}
namespace detail {
/// \brief Log a `message` with severity `severity`.
inline void log(LogSeverity severity,
const std::string& message,
const std::string& file,
int line) {
auto& logger = get_global_logger();
logger.log(severity, message, file, line);
}
} // namespace detail
#if HIGHFIVE_LOG_LEVEL <= HIGHFIVE_LOG_LEVEL_DEBUG
#define HIGHFIVE_LOG_DEBUG(message) \
::HighFive::detail::log(::HighFive::LogSeverity::Debug, (message), __FILE__, __LINE__);
// Useful, for the common pattern: if ...; then log something.
#define HIGHFIVE_LOG_DEBUG_IF(cond, message) \
if ((cond)) { \
HIGHFIVE_LOG_DEBUG((message)); \
}
#else
#define HIGHFIVE_LOG_DEBUG(message) ;
#define HIGHFIVE_LOG_DEBUG_IF(cond, message) ;
#endif
#if HIGHFIVE_LOG_LEVEL <= HIGHFIVE_LOG_LEVEL_INFO
#define HIGHFIVE_LOG_INFO(message) \
::HighFive::detail::log(::HighFive::LogSeverity::Info, (message), __FILE__, __LINE__);
// Useful, for the common pattern: if ...; then log something.
#define HIGHFIVE_LOG_INFO_IF(cond, message) \
if ((cond)) { \
HIGHFIVE_LOG_INFO((message)); \
}
#else
#define HIGHFIVE_LOG_INFO(message) ;
#define HIGHFIVE_LOG_INFO_IF(cond, message) ;
#endif
#if HIGHFIVE_LOG_LEVEL <= HIGHFIVE_LOG_LEVEL_WARN
#define HIGHFIVE_LOG_WARN(message) \
::HighFive::detail::log(::HighFive::LogSeverity::Warn, (message), __FILE__, __LINE__);
// Useful, for the common pattern: if ...; then log something.
#define HIGHFIVE_LOG_WARN_IF(cond, message) \
if ((cond)) { \
HIGHFIVE_LOG_WARN((message)); \
}
#else
#define HIGHFIVE_LOG_WARN(message) ;
#define HIGHFIVE_LOG_WARN_IF(cond, message) ;
#endif
#if HIGHFIVE_LOG_LEVEL <= HIGHFIVE_LOG_LEVEL_ERROR
#define HIGHFIVE_LOG_ERROR(message) \
::HighFive::detail::log(::HighFive::LogSeverity::Error, (message), __FILE__, __LINE__);
// Useful, for the common pattern: if ...; then log something.
#define HIGHFIVE_LOG_ERROR_IF(cond, message) \
if ((cond)) { \
HIGHFIVE_LOG_ERROR((message)); \
}
#else
#define HIGHFIVE_LOG_ERROR(message) ;
#define HIGHFIVE_LOG_ERROR_IF(cond, message) ;
#endif
} // namespace HighFive

View File

@ -0,0 +1,33 @@
/*
* Copyright (c), 2020
*
* 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)
*
*/
#pragma once
#define HIGHFIVE_VERSION_MAJOR 3
#define HIGHFIVE_VERSION_MINOR 0
#define HIGHFIVE_VERSION_PATCH 0
/** \brief Concatenated representation of the HighFive version.
*
* \warning The macro `HIGHFIVE_VERSION` by itself isn't valid C/C++.
*
* However, it can be stringified with two layers of macros, e.g.,
* \code{.cpp}
* #define STRINGIFY_VALUE(s) STRINGIFY_NAME(s)
* #define STRINGIFY_NAME(s) #s
*
* std::cout << STRINGIFY_VALUE(HIGHFIVE_VERSION) << "\n";
* \endcode
*/
#define HIGHFIVE_VERSION 3.0.0
/** \brief String representation of the HighFive version.
*
* \warning This macro only exists from 2.7.1 onwards.
*/
#define HIGHFIVE_VERSION_STRING "3.0.0"

View File

@ -0,0 +1,81 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include "../H5Attribute.hpp"
namespace HighFive {
template <typename Derivate>
class AnnotateTraits {
public:
///
/// \brief create a new attribute with the name attribute_name
/// \param attribute_name identifier of the attribute
/// \param space Associated \ref DataSpace
/// \param type
/// \return the attribute object
///
Attribute createAttribute(const std::string& attribute_name,
const DataSpace& space,
const DataType& type);
///
/// \brief createAttribute create a new attribute on the current dataset with
/// size specified by space
/// \param attribute_name identifier of the attribute
/// \param space Associated DataSpace
/// informations
/// \return Attribute Object
template <typename Type>
Attribute createAttribute(const std::string& attribute_name, const DataSpace& space);
///
/// \brief createAttribute create a new attribute on the current dataset and
/// write to it, inferring the DataSpace from data.
/// \param attribute_name identifier of the attribute
/// \param data Associated data to write, must support DataSpace::From, see
/// \ref DataSpace for more information
/// \return Attribute Object
///
template <typename T>
Attribute createAttribute(const std::string& attribute_name, const T& data);
///
/// \brief deleteAttribute let you delete an attribute by its name.
/// \param attribute_name identifier of the attribute
void deleteAttribute(const std::string& attribute_name);
///
/// \brief open an existing attribute with the name attribute_name
/// \param attribute_name identifier of the attribute
/// \return the attribute object
Attribute getAttribute(const std::string& attribute_name) const;
///
/// \brief return the number of attributes of the node / group
/// \return number of attributes
size_t getNumberAttributes() const;
///
/// \brief list all attribute name of the node / group
/// \return number of attributes
std::vector<std::string> listAttributeNames() const;
///
/// \brief checks an attribute exists
/// \return number of attributes
bool hasAttribute(const std::string& attr_name) const;
private:
using derivate_type = Derivate;
};
} // namespace HighFive

View File

@ -0,0 +1,97 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include <vector>
#include <H5Apublic.h>
#include <H5Ppublic.h>
#include "H5Attribute_misc.hpp"
#include "H5Iterables_misc.hpp"
#include "h5a_wrapper.hpp"
namespace HighFive {
template <typename Derivate>
inline Attribute AnnotateTraits<Derivate>::createAttribute(const std::string& attribute_name,
const DataSpace& space,
const DataType& dtype) {
auto attr_id = detail::h5a_create2(static_cast<Derivate*>(this)->getId(),
attribute_name.c_str(),
dtype.getId(),
space.getId(),
H5P_DEFAULT,
H5P_DEFAULT);
return detail::make_attribute(attr_id);
}
template <typename Derivate>
template <typename Type>
inline Attribute AnnotateTraits<Derivate>::createAttribute(const std::string& attribute_name,
const DataSpace& space) {
return createAttribute(attribute_name, space, create_and_check_datatype<Type>());
}
template <typename Derivate>
template <typename T>
inline Attribute AnnotateTraits<Derivate>::createAttribute(const std::string& attribute_name,
const T& data) {
Attribute att =
createAttribute(attribute_name,
DataSpace::From(data),
create_and_check_datatype<typename details::inspector<T>::base_type>());
att.write(data);
return att;
}
template <typename Derivate>
inline void AnnotateTraits<Derivate>::deleteAttribute(const std::string& attribute_name) {
detail::h5a_delete(static_cast<const Derivate*>(this)->getId(), attribute_name.c_str());
}
template <typename Derivate>
inline Attribute AnnotateTraits<Derivate>::getAttribute(const std::string& attribute_name) const {
const auto attr_id = detail::h5a_open(static_cast<const Derivate*>(this)->getId(),
attribute_name.c_str(),
H5P_DEFAULT);
return detail::make_attribute(attr_id);
}
template <typename Derivate>
inline size_t AnnotateTraits<Derivate>::getNumberAttributes() const {
int res = detail::h5a_get_num_attrs(static_cast<const Derivate*>(this)->getId());
return static_cast<size_t>(res);
}
template <typename Derivate>
inline std::vector<std::string> AnnotateTraits<Derivate>::listAttributeNames() const {
std::vector<std::string> names;
details::HighFiveIterateData iterateData(names);
size_t num_objs = getNumberAttributes();
names.reserve(num_objs);
detail::h5a_iterate2(static_cast<const Derivate*>(this)->getId(),
H5_INDEX_NAME,
H5_ITER_INC,
nullptr,
&details::internal_high_five_iterate<H5A_info_t>,
static_cast<void*>(&iterateData));
return names;
}
template <typename Derivate>
inline bool AnnotateTraits<Derivate>::hasAttribute(const std::string& attr_name) const {
return detail::h5a_exists(static_cast<const Derivate*>(this)->getId(), attr_name.c_str()) > 0;
}
} // namespace HighFive

View File

@ -0,0 +1,183 @@
/*
* Copyright (c), 2017, Ali Can Demiralp <ali.demiralp@rwth-aachen.de>
*
* 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)
*
*/
#pragma once
#include <H5Ipublic.h>
#include <algorithm>
#include <functional>
#include <numeric>
#include <sstream>
#include <string>
#include <H5Ppublic.h>
#include "../H5DataSpace.hpp"
#include "H5Converter_misc.hpp"
#include "H5Inspector_misc.hpp"
#include "H5ReadWrite_misc.hpp"
#include "H5Utils.hpp"
#include "h5a_wrapper.hpp"
#include "h5d_wrapper.hpp"
#include "squeeze.hpp"
#include "assert_compatible_spaces.hpp"
namespace HighFive {
inline std::string Attribute::getName() const {
return details::get_name(
[&](char* buffer, size_t length) { return detail::h5a_get_name(_hid, length, buffer); });
}
inline size_t Attribute::getStorageSize() const {
if (!this->isValid()) {
throw AttributeException("Invalid call to `DataSet::getFile` for invalid object");
}
return static_cast<size_t>(detail::h5a_get_storage_size(_hid));
}
inline DataType Attribute::getDataType() const {
DataType res;
res._hid = detail::h5a_get_type(_hid);
return res;
}
inline DataSpace Attribute::getSpace() const {
DataSpace space;
space._hid = detail::h5a_get_space(_hid);
return space;
}
inline DataSpace Attribute::getMemSpace() const {
return _mem_space.getId() == H5I_INVALID_HID ? getSpace() : _mem_space;
}
template <typename T>
inline T Attribute::read() const {
T array;
read(array);
return array;
}
template <typename T>
inline void Attribute::read(T& array) const {
const DataSpace& mem_space = getMemSpace();
auto file_datatype = getDataType();
const details::BufferInfo<T> buffer_info(
file_datatype,
[this]() -> std::string { return this->getName(); },
details::BufferInfo<T>::Operation::read);
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to read attribute of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions: " << buffer_info.getMinRank() << "(min) to "
<< buffer_info.getMaxRank() << "(max)";
throw DataSpaceException(ss.str());
}
auto dims = mem_space.getDimensions();
if (mem_space.getElementCount() == 0) {
details::inspector<T>::prepare(array, dims);
return;
}
auto r = details::data_converter::get_reader<T>(dims, array, file_datatype);
read_raw(r.getPointer(), buffer_info.data_type);
// re-arrange results
r.unserialize(array);
auto t = buffer_info.data_type;
auto c = t.getClass();
if (c == DataTypeClass::VarLen || t.isVariableStr()) {
#if H5_VERSION_GE(1, 12, 0)
// This one have been created in 1.12.0
(void) detail::h5t_reclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.getPointer());
#else
// This one is deprecated since 1.12.0
(void) detail::h5d_vlen_reclaim(t.getId(), mem_space.getId(), H5P_DEFAULT, r.getPointer());
#endif
}
}
template <typename T>
inline void Attribute::read_raw(T* array, const DataType& mem_datatype) const {
static_assert(!std::is_const<T>::value,
"read() requires a non-const structure to read data into");
detail::h5a_read(getId(), mem_datatype.getId(), static_cast<void*>(array));
}
template <typename T>
inline void Attribute::read_raw(T* array) const {
using element_type = typename details::inspector<T>::base_type;
const DataType& mem_datatype = create_and_check_datatype<element_type>();
read_raw(array, mem_datatype);
}
template <typename T>
inline void Attribute::write(const T& buffer) {
const DataSpace& mem_space = getMemSpace();
auto dims = mem_space.getDimensions();
if (mem_space.getElementCount() == 0) {
return;
}
auto file_datatype = getDataType();
const details::BufferInfo<T> buffer_info(
file_datatype,
[this]() -> std::string { return this->getName(); },
details::BufferInfo<T>::Operation::write);
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to write attribute of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions: " << buffer_info.getMinRank() << "(min) to "
<< buffer_info.getMaxRank() << "(max)";
throw DataSpaceException(ss.str());
}
auto w = details::data_converter::serialize<T>(buffer, dims, file_datatype);
write_raw(w.getPointer(), buffer_info.data_type);
}
template <typename T>
inline void Attribute::write_raw(const T* buffer, const DataType& mem_datatype) {
detail::h5a_write(getId(), mem_datatype.getId(), buffer);
}
template <typename T>
inline void Attribute::write_raw(const T* buffer) {
using element_type = typename details::inspector<T>::base_type;
const auto& mem_datatype = create_and_check_datatype<element_type>();
write_raw(buffer, mem_datatype);
}
inline Attribute Attribute::squeezeMemSpace(const std::vector<size_t>& axes) const {
auto mem_dims = this->getMemSpace().getDimensions();
auto squeezed_dims = detail::squeeze(mem_dims, axes);
auto attr = *this;
attr._mem_space = DataSpace(squeezed_dims);
return attr;
}
inline Attribute Attribute::reshapeMemSpace(const std::vector<size_t>& new_dims) const {
detail::assert_compatible_spaces(this->getMemSpace(), new_dims);
auto attr = *this;
attr._mem_space = DataSpace(new_dims);
return attr;
}
} // namespace HighFive

View File

@ -0,0 +1,426 @@
/*
* Copyright (c) 2022 Blue Brain Project
*
* 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)
*
*/
#pragma once
#include <type_traits>
#include "H5Inspector_misc.hpp"
#include "../H5DataType.hpp"
namespace HighFive {
namespace details {
template <class T>
struct is_std_string {
static constexpr bool value =
std::is_same<typename inspector<T>::base_type, std::string>::value;
};
template <class T, class V = void>
struct enable_shallow_copy
: public std::enable_if<!is_std_string<T>::value && inspector<T>::is_trivially_copyable, V> {};
template <class T, class V = void>
struct enable_deep_copy
: public std::enable_if<!is_std_string<T>::value && !inspector<T>::is_trivially_copyable, V> {};
template <class T, class V = void>
struct enable_string_copy: public std::enable_if<is_std_string<T>::value, V> {};
template <typename T, bool IsReadOnly>
struct ShallowCopyBuffer {
using type = unqualified_t<T>;
using hdf5_type =
typename std::conditional<IsReadOnly,
typename std::add_const<typename inspector<T>::hdf5_type>::type,
typename inspector<T>::hdf5_type>::type;
ShallowCopyBuffer() = delete;
explicit ShallowCopyBuffer(typename std::conditional<IsReadOnly, const T&, T&>::type val)
: ptr(inspector<T>::data(val)) {};
hdf5_type* getPointer() const {
return ptr;
}
hdf5_type* begin() const {
return getPointer();
}
void unserialize(T& /* val */) const {
/* nothing to do. */
}
private:
hdf5_type* ptr;
};
template <class T>
struct DeepCopyBuffer {
using type = unqualified_t<T>;
using hdf5_type = typename inspector<type>::hdf5_type;
explicit DeepCopyBuffer(const std::vector<size_t>& _dims)
: buffer(compute_total_size(_dims))
, dims(_dims) {}
hdf5_type* getPointer() {
return buffer.data();
}
hdf5_type const* getPointer() const {
return buffer.data();
}
hdf5_type* begin() {
return getPointer();
}
hdf5_type const* begin() const {
return getPointer();
}
void unserialize(T& val) const {
inspector<type>::unserialize(buffer.data(), dims, val);
}
private:
std::vector<hdf5_type> buffer;
std::vector<size_t> dims;
};
enum class BufferMode { Read, Write };
///
/// \brief String length in bytes excluding the `\0`.
///
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_size;
}
///
/// \brief A buffer for reading/writing strings.
///
/// A string in HDF5 can be represented as a fixed or variable length string.
/// The important difference for this buffer is that `H5D{read,write}` expects
/// different input depending on whether the strings are fixed or variable length.
/// For fixed length strings, it expects an array of chars, i.e. one string
/// packed after the other contiguously. While for variable length strings it
/// expects a list of pointers to the beginning of each string. Variable length
/// string must be null-terminated; because that's how their length is
/// determined.
///
/// This buffer hides the difference between fixed and variable length strings
/// by having internal data structures available for both cases at compile time.
/// The choice which internal buffer to use is made at runtime.
///
/// Consider an HDF5 dataset with N fixed-length strings, each of which is M
/// characters long. Then the in-memory strings are copied into an internal
/// buffer of size N*M. If null- or space-padded the buffer should be filled
/// with the appropriate character. This is important if the in-memory strings
/// are less than M characters long.
///
/// An HDF5 dataset with N variable-length strings (all null-terminated) uses
/// the internal list of pointers to the beginning of each string. Those
/// pointers can either point to the in-memory strings themselves, if those
/// strings are known to be null-terminated. Otherwise the in-memory strings are
/// copied to an internal buffer of null-terminated strings; and the pointer
/// points to the start of the string in the internal buffer.
///
/// This class is responsible for arranging the strings properly before passing
/// the buffers to HDF5. To keep this class generic, it provides a generic
/// read/write interface to the internal strings, i.e. a pointer with a size.
/// For reading from the buffer the proxy is called `StringConstView`. This
/// proxy object is to be used by the `inspector` to copy from the buffer into
/// the final destination, e.g. an `std::string`. Similarly, there's a proxy
/// object for serializing into the buffer, i.e. the `StringView`. Again the
/// `inspector` is responsible for obtaining the pointer, size and padding of
/// the string.
///
/// Nomenclature:
/// - size of a string is the number of bytes required to store the string,
/// including the null character for null-terminated strings.
///
/// - length of a string is the number of bytes without the null character.
///
/// Note: both 'length' and 'size' are counted in number of bytes, not number
/// of symbols or characters. Even for UTF8 strings.
template <typename T, BufferMode buffer_mode>
struct StringBuffer {
using type = unqualified_t<T>;
using hdf5_type = typename inspector<type>::hdf5_type;
class StringView {
public:
StringView(StringBuffer<T, buffer_mode>& _buffer, size_t _i)
: buffer(_buffer)
, i(_i) {}
///
/// \brief Assign the in-memory string to the buffer.
///
/// This method copies the in-memory string to the appropriate
/// internal buffer as needed.
///
/// The `length` is the length of the string in bytes.
void assign(char const* data, size_t length, StringPadding pad) {
if (buffer.isVariableLengthString()) {
if (pad == StringPadding::NullTerminated) {
buffer.variable_length_pointers[i] = data;
} else {
buffer.variable_length_buffer[i] = std::string(data, length);
buffer.variable_length_pointers[i] = buffer.variable_length_buffer[i].data();
}
} 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_max_length) {
throw std::invalid_argument("String length too big.");
}
memcpy(&buffer.fixed_length_buffer[i * buffer.string_size], data, length);
}
}
private:
StringBuffer<T, buffer_mode>& buffer;
size_t i;
};
class StringConstView {
public:
StringConstView(const StringBuffer<T, buffer_mode>& _buffer, size_t _i)
: buffer(_buffer)
, i(_i) {}
/// \brief Pointer to the first byte of the string.
///
/// The valid indices for this pointer are: 0, ..., length() - 1.
char const* data() const {
if (buffer.isVariableLengthString()) {
return buffer.variable_length_pointers[i];
} else {
return &buffer.fixed_length_buffer[i * buffer.string_size];
}
}
/// \brief Length of the string in bytes.
///
/// Note that for null-terminated strings the "length" doesn't include
/// the null character. Hence, if storing this string as a
/// null-terminated string, the destination buffer needs to be at least
/// `length() + 1` bytes long.
size_t length() const {
if (buffer.isNullTerminated()) {
return char_buffer_length(data(), buffer.string_size);
} else {
return buffer.string_max_length;
}
}
private:
const StringBuffer<T, buffer_mode>& buffer;
size_t i;
};
class Iterator {
public:
Iterator(StringBuffer<T, buffer_mode>& _buffer, size_t _pos)
: buffer(_buffer)
, pos(_pos) {}
Iterator operator+(size_t n_strings) const {
return Iterator(buffer, pos + n_strings);
}
void operator+=(size_t n_strings) {
pos += n_strings;
}
StringView operator*() {
return StringView(buffer, pos);
}
StringConstView operator*() const {
return StringConstView(buffer, pos);
}
private:
StringBuffer<T, buffer_mode>& buffer;
size_t pos;
};
StringBuffer(std::vector<size_t> _dims, const DataType& _file_datatype)
: file_datatype(_file_datatype.asStringType())
, padding(file_datatype.getPadding())
, string_size(file_datatype.isVariableStr() ? size_t(-1) : file_datatype.getSize())
, string_max_length(string_size - size_t(isNullTerminated()))
, dims(_dims) {
if (string_size == 0 && isNullTerminated()) {
throw DataTypeException(
"Fixed-length, null-terminated need at least one byte to store the "
"null-character.");
}
auto n_strings = compute_total_size(dims);
if (isVariableLengthString()) {
variable_length_buffer.resize(n_strings);
variable_length_pointers.resize(n_strings);
} else {
char pad = padding == StringPadding::SpacePadded ? ' ' : '\0';
fixed_length_buffer.assign(n_strings * string_size, pad);
}
}
bool isVariableLengthString() const {
return file_datatype.isVariableStr();
}
bool isFixedLengthString() const {
return file_datatype.isFixedLenStr();
}
bool isNullTerminated() const {
return file_datatype.getPadding() == StringPadding::NullTerminated;
}
void* getPointer() {
if (file_datatype.isVariableStr()) {
return variable_length_pointers.data();
} else {
return fixed_length_buffer.data();
}
}
Iterator begin() {
return Iterator(*this, 0ul);
}
void unserialize(T& val) {
inspector<type>::unserialize(begin(), dims, val);
}
private:
StringType file_datatype;
StringPadding padding;
// 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;
std::vector<std::string> variable_length_buffer;
std::vector<
typename std::conditional<buffer_mode == BufferMode::Write, const char, char>::type*>
variable_length_pointers;
};
template <typename T, typename Enable = void>
struct Writer;
template <typename T>
struct Writer<T, typename enable_shallow_copy<T>::type>: public ShallowCopyBuffer<T, true> {
private:
using super = ShallowCopyBuffer<T, true>;
public:
explicit Writer(const T& val,
const std::vector<size_t>& /* dims */,
const DataType& /* file_datatype */)
: super(val) {};
};
template <typename T>
struct Writer<T, typename enable_deep_copy<T>::type>: public DeepCopyBuffer<T> {
explicit Writer(const T& val,
const std::vector<size_t>& _dims,
const DataType& /* file_datatype */)
: DeepCopyBuffer<T>(_dims) {
inspector<T>::serialize(val, _dims, this->begin());
}
};
template <typename T>
struct Writer<T, typename enable_string_copy<T>::type>: public StringBuffer<T, BufferMode::Write> {
explicit Writer(const T& val, const std::vector<size_t>& _dims, const DataType& _file_datatype)
: StringBuffer<T, BufferMode::Write>(_dims, _file_datatype) {
inspector<T>::serialize(val, _dims, this->begin());
}
};
template <typename T, typename Enable = void>
struct Reader;
template <typename T>
struct Reader<T, typename enable_shallow_copy<T>::type>: public ShallowCopyBuffer<T, false> {
private:
using super = ShallowCopyBuffer<T, false>;
using type = typename super::type;
public:
Reader(const std::vector<size_t>&, type& val, const DataType& /* file_datatype */)
: super(val) {}
};
template <typename T>
struct Reader<T, typename enable_deep_copy<T>::type>: public DeepCopyBuffer<T> {
private:
using super = DeepCopyBuffer<T>;
using type = typename super::type;
public:
Reader(const std::vector<size_t>& _dims, type&, const DataType& /* file_datatype */)
: super(_dims) {}
};
template <typename T>
struct Reader<T, typename enable_string_copy<T>::type>: public StringBuffer<T, BufferMode::Write> {
public:
explicit Reader(const std::vector<size_t>& _dims,
const T& /* val */,
const DataType& _file_datatype)
: StringBuffer<T, BufferMode::Write>(_dims, _file_datatype) {}
};
struct data_converter {
template <typename T>
static Writer<T> serialize(const typename inspector<T>::type& val,
const std::vector<size_t>& dims,
const DataType& file_datatype) {
return Writer<T>(val, dims, file_datatype);
}
template <typename T>
static Reader<T> get_reader(const std::vector<size_t>& dims,
T& val,
const DataType& file_datatype) {
inspector<T>::prepare(val, dims);
return Reader<T>(dims, val, file_datatype);
}
};
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,62 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <algorithm>
#include <functional>
#include <numeric>
#include <sstream>
#include <string>
#include <H5Ppublic.h>
#include "h5d_wrapper.hpp"
#include "H5Utils.hpp"
namespace HighFive {
inline uint64_t DataSet::getStorageSize() const {
if (!this->isValid()) {
throw DataSetException("Invalid call to `DataSet::getStorageSize` for invalid object");
}
return detail::h5d_get_storage_size(_hid);
}
inline DataType DataSet::getDataType() const {
return DataType(detail::h5d_get_type(_hid));
}
inline DataSpace DataSet::getSpace() const {
DataSpace space;
space._hid = detail::h5d_get_space(_hid);
return space;
}
inline DataSpace DataSet::getMemSpace() const {
return getSpace();
}
inline uint64_t DataSet::getOffset() const {
return static_cast<uint64_t>(detail::h5d_get_offset(_hid));
}
inline void DataSet::resize(const std::vector<size_t>& dims) {
const size_t numDimensions = getSpace().getDimensions().size();
if (dims.size() != numDimensions) {
HDF5ErrMapper::ToException<DataSetException>("Invalid dataspace dimensions, got " +
std::to_string(dims.size()) + " expected " +
std::to_string(numDimensions));
}
std::vector<hsize_t> real_dims(dims.begin(), dims.end());
detail::h5d_set_extent(getId(), real_dims.data());
}
} // namespace HighFive

View File

@ -0,0 +1,474 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include <complex>
#include <cstring>
#if HIGHFIVE_CXX_STD >= 17
#include <cstddef>
#endif
#include <H5Ppublic.h>
#include "H5Inspector_misc.hpp"
#include "h5t_wrapper.hpp"
#include "h5i_wrapper.hpp"
namespace HighFive {
namespace { // unnamed
inline DataTypeClass convert_type_class(const H5T_class_t& tclass);
inline std::string type_class_string(DataTypeClass);
inline hid_t create_string(std::size_t length);
} // namespace
inline bool DataType::empty() const noexcept {
return _hid == H5I_INVALID_HID;
}
inline DataTypeClass DataType::getClass() const {
return convert_type_class(detail::h5t_get_class(_hid));
}
inline size_t DataType::getSize() const {
return detail::h5t_get_size(_hid);
}
inline bool DataType::operator==(const DataType& other) const {
return detail::h5t_equal(_hid, other._hid) > 0;
}
inline bool DataType::operator!=(const DataType& other) const {
return !(*this == other);
}
inline bool DataType::isVariableStr() const {
return detail::h5t_is_variable_str(_hid) > 0;
}
inline bool DataType::isFixedLenStr() const {
return getClass() == DataTypeClass::String && !isVariableStr();
}
inline bool DataType::isReference() const {
return detail::h5t_equal(_hid, H5T_STD_REF_OBJ) > 0;
}
inline StringType DataType::asStringType() const {
if (getClass() != DataTypeClass::String) {
throw DataTypeException("Invalid conversion to StringType.");
}
if (isValid()) {
detail::h5i_inc_ref(_hid);
}
return StringType(_hid);
}
inline std::string DataType::string() const {
return type_class_string(getClass()) + std::to_string(getSize() * 8);
}
inline StringPadding StringType::getPadding() const {
return StringPadding(detail::h5t_get_strpad(_hid));
}
inline CharacterSet StringType::getCharacterSet() const {
return CharacterSet(detail::h5t_get_cset(_hid));
}
inline FixedLengthStringType::FixedLengthStringType(size_t size,
StringPadding padding,
CharacterSet character_set) {
if (size == 0 && padding == StringPadding::NullTerminated) {
throw DataTypeException(
"Fixed-length, null-terminated need at least one byte to store the null-character.");
}
_hid = detail::h5t_copy(H5T_C_S1);
detail::h5t_set_size(_hid, hsize_t(size));
detail::h5t_set_cset(_hid, H5T_cset_t(character_set));
detail::h5t_set_strpad(_hid, H5T_str_t(padding));
}
inline VariableLengthStringType::VariableLengthStringType(CharacterSet character_set) {
_hid = detail::h5t_copy(H5T_C_S1);
detail::h5t_set_size(_hid, H5T_VARIABLE);
detail::h5t_set_cset(_hid, H5T_cset_t(character_set));
}
// char mapping
template <>
inline AtomicType<char>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_CHAR);
}
template <>
inline AtomicType<signed char>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_SCHAR);
}
template <>
inline AtomicType<unsigned char>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_UCHAR);
}
// short mapping
template <>
inline AtomicType<short>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_SHORT);
}
template <>
inline AtomicType<unsigned short>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_USHORT);
}
// integer mapping
template <>
inline AtomicType<int>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_INT);
}
template <>
inline AtomicType<unsigned>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_UINT);
}
// long mapping
template <>
inline AtomicType<long>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_LONG);
}
template <>
inline AtomicType<unsigned long>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_ULONG);
}
// long long mapping
template <>
inline AtomicType<long long>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_LLONG);
}
template <>
inline AtomicType<unsigned long long>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_ULLONG);
}
// half-float, float, double and long double mapping
template <>
inline AtomicType<float>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_FLOAT);
}
template <>
inline AtomicType<double>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_DOUBLE);
}
template <>
inline AtomicType<long double>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_LDOUBLE);
}
// std string
template <>
inline AtomicType<std::string>::AtomicType() {
_hid = create_string(H5T_VARIABLE);
}
#if HIGHFIVE_CXX_STD >= 17
// std byte
template <>
inline AtomicType<std::byte>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_B8);
}
#endif
// Fixed-Length strings
// require class specialization templated for the char length
template <size_t StrLen>
class AtomicType<char[StrLen]>: public DataType {
public:
inline AtomicType()
: DataType(create_string(StrLen)) {}
};
template <typename T>
class AtomicType<std::complex<T>>: public DataType {
public:
inline AtomicType()
: DataType(
CompoundType({{"r", create_datatype<T>(), 0}, {"i", create_datatype<T>(), sizeof(T)}},
sizeof(std::complex<T>))) {
static_assert(std::is_arithmetic<T>::value,
"std::complex accepts only floating point and integral numbers.");
}
};
// For boolean we act as h5py
inline EnumType<details::Boolean> create_enum_boolean() {
return {{"FALSE", details::Boolean::HighFiveFalse}, {"TRUE", details::Boolean::HighFiveTrue}};
}
// Other cases not supported. Fail early with a user message
template <typename T>
AtomicType<T>::AtomicType() {
static_assert(
true,
"Missing specialization of AtomicType<T>. Therefore, type T is not supported by HighFive.");
}
// Internal
// Reference mapping
template <>
inline AtomicType<Reference>::AtomicType() {
_hid = detail::h5t_copy(H5T_STD_REF_OBJ);
}
inline size_t find_first_atomic_member_size(hid_t hid) {
// Recursive exit condition
if (detail::h5t_get_class(hid) == H5T_COMPOUND) {
auto number_of_members = detail::h5t_get_nmembers(hid);
if (number_of_members == -1) {
throw DataTypeException("Cannot get members of CompoundType with hid: " +
std::to_string(hid));
}
if (number_of_members == 0) {
throw DataTypeException("No members defined for CompoundType with hid: " +
std::to_string(hid));
}
auto member_type = detail::h5t_get_member_type(hid, 0);
auto size = find_first_atomic_member_size(member_type);
detail::h5t_close(member_type);
return size;
} else if (detail::h5t_get_class(hid) == H5T_STRING) {
return 1;
}
return detail::h5t_get_size(hid);
}
namespace detail {
// Calculate the padding required to align an element of a struct
// For padding see explanation here: https://en.cppreference.com/w/cpp/language/object#Alignment
// It is to compute padding following last element inserted inside a struct
// 1) We want to push back an element padded to the structure
// 'current_size' is the size of the structure before adding the new element.
// 'member_size' the size of the element we want to add.
// 2) We want to compute the final padding for the global structure
// 'current_size' is the size of the whole structure without final padding
// 'member_size' is the maximum size of all element of the struct
//
// The basic formula is only to know how much we need to add to 'current_size' to fit
// 'member_size'.
// And at the end, we do another computation because the end padding, should fit the biggest
// element of the struct.
//
// As we are with `size_t` element, we need to compute everything inside R+
inline size_t struct_padding(size_t current_size, size_t member_size) {
if (member_size == 0) {
throw DataTypeException("Unexpected `member_size == 0`.");
}
return member_size >= current_size
? (member_size - current_size) % member_size
: ((member_size - ((current_size - member_size) % member_size))) % member_size;
}
} // namespace detail
inline void CompoundType::create(size_t size) {
if (size == 0) {
size_t current_size = 0, max_atomic_size = 0;
// Do a first pass to find the total size of the compound datatype
for (auto& member: members) {
size_t member_size = detail::h5t_get_size(member.base_type.getId());
if (member_size == 0) {
throw DataTypeException("Cannot get size of DataType with hid: " +
std::to_string(member.base_type.getId()));
}
size_t first_atomic_size = find_first_atomic_member_size(member.base_type.getId());
// Set the offset of this member within the struct according to the
// standard alignment rules. The c++ standard specifies that:
// > objects have an alignment requirement of which their size is a multiple
member.offset = current_size + detail::struct_padding(current_size, first_atomic_size);
// Set the current size to the end of the new member
current_size = member.offset + member_size;
// Keep track of the highest atomic member size because it's needed
// for the padding of the complete compound type.
max_atomic_size = std::max(max_atomic_size, first_atomic_size);
}
size = current_size + detail::struct_padding(current_size, max_atomic_size);
}
// Create the HDF5 type
_hid = detail::h5t_create(H5T_COMPOUND, size);
// Loop over all the members and insert them into the datatype
for (const auto& member: members) {
detail::h5t_insert(_hid, member.name.c_str(), member.offset, member.base_type.getId());
}
}
inline void CompoundType::commit(const Object& object, const std::string& name) const {
detail::h5t_commit2(
object.getId(), name.c_str(), getId(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
}
template <typename T>
inline void EnumType<T>::create() {
// Create the HDF5 type
_hid = detail::h5t_enum_create(AtomicType<typename std::underlying_type<T>::type>{}.getId());
// Loop over all the members and insert them into the datatype
for (const auto& member: members) {
detail::h5t_enum_insert(_hid, member.name.c_str(), &(member.value));
}
}
template <typename T>
inline void EnumType<T>::commit(const Object& object, const std::string& name) const {
detail::h5t_commit2(
object.getId(), name.c_str(), getId(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
}
namespace {
inline hid_t create_string(size_t length) {
hid_t _hid = detail::h5t_copy(H5T_C_S1);
detail::h5t_set_size(_hid, length);
detail::h5t_set_cset(_hid, H5T_CSET_UTF8);
return _hid;
}
inline DataTypeClass convert_type_class(const H5T_class_t& tclass) {
switch (tclass) {
case H5T_TIME:
return DataTypeClass::Time;
case H5T_INTEGER:
return DataTypeClass::Integer;
case H5T_FLOAT:
return DataTypeClass::Float;
case H5T_STRING:
return DataTypeClass::String;
case H5T_BITFIELD:
return DataTypeClass::BitField;
case H5T_OPAQUE:
return DataTypeClass::Opaque;
case H5T_COMPOUND:
return DataTypeClass::Compound;
case H5T_REFERENCE:
return DataTypeClass::Reference;
case H5T_ENUM:
return DataTypeClass::Enum;
case H5T_VLEN:
return DataTypeClass::VarLen;
case H5T_ARRAY:
return DataTypeClass::Array;
case H5T_NO_CLASS:
case H5T_NCLASSES:
default:
return DataTypeClass::Invalid;
}
}
inline std::string type_class_string(DataTypeClass tclass) {
switch (tclass) {
case DataTypeClass::Time:
return "Time";
case DataTypeClass::Integer:
return "Integer";
case DataTypeClass::Float:
return "Float";
case DataTypeClass::String:
return "String";
case DataTypeClass::BitField:
return "BitField";
case DataTypeClass::Opaque:
return "Opaque";
case DataTypeClass::Compound:
return "Compound";
case DataTypeClass::Reference:
return "Reference";
case DataTypeClass::Enum:
return "Enum";
case DataTypeClass::VarLen:
return "Varlen";
case DataTypeClass::Array:
return "Array";
default:
return "(Invalid)";
}
}
} // unnamed namespace
/// \brief Create a DataType instance representing type T
template <typename T>
inline DataType create_datatype() {
return AtomicType<T>();
}
/// \brief Create a DataType instance representing type T and perform a sanity check on its size
template <typename T>
inline DataType create_and_check_datatype() {
DataType t = create_datatype<T>();
if (t.empty()) {
throw DataTypeException("Type given to create_and_check_datatype is not valid");
}
// Skip check if the base type is a variable length string
if (t.isVariableStr()) {
return t;
}
// Check that the size of the template type matches the size that HDF5 is
// expecting.
if (t.isReference() || t.isFixedLenStr()) {
return t;
}
if (sizeof(T) != t.getSize()) {
std::ostringstream ss;
ss << "Size of array type " << sizeof(T) << " != that of memory datatype " << t.getSize()
<< std::endl;
throw DataTypeException(ss.str());
}
return t;
}
} // namespace HighFive
HIGHFIVE_REGISTER_TYPE(HighFive::details::Boolean, HighFive::create_enum_boolean)
namespace HighFive {
template <>
inline DataType create_datatype<bool>() {
return create_datatype<HighFive::details::Boolean>();
}
} // namespace HighFive

View File

@ -0,0 +1,146 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <array>
#include <initializer_list>
#include <vector>
#include <numeric>
#include <H5Spublic.h>
#include "H5Utils.hpp"
#include "H5Converter_misc.hpp"
#include "h5s_wrapper.hpp"
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()) {}
template <size_t N>
inline DataSpace::DataSpace(const std::array<size_t, N>& dims)
: DataSpace(dims.begin(), dims.end()) {}
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)...}) {}
template <class IT, typename>
inline DataSpace::DataSpace(const IT begin, const IT end) {
std::vector<hsize_t> real_dims(begin, end);
_hid = detail::h5s_create_simple(int(real_dims.size()), real_dims.data(), nullptr);
}
inline DataSpace DataSpace::Scalar() {
return DataSpace(DataSpace::dataspace_scalar);
}
inline DataSpace DataSpace::Null() {
return DataSpace(DataSpace::dataspace_null);
}
inline DataSpace::DataSpace(const std::vector<size_t>& dims, const std::vector<size_t>& maxdims) {
if (dims.size() != maxdims.size()) {
throw DataSpaceException("dims and maxdims must be the same length.");
}
std::vector<hsize_t> real_dims(dims.begin(), dims.end());
std::vector<hsize_t> real_maxdims(maxdims.begin(), maxdims.end());
// Replace unlimited flag with actual HDF one
std::replace(real_maxdims.begin(),
real_maxdims.end(),
static_cast<hsize_t>(DataSpace::UNLIMITED),
H5S_UNLIMITED);
_hid = detail::h5s_create_simple(int(dims.size()), real_dims.data(), real_maxdims.data());
}
inline DataSpace::DataSpace(DataSpace::DataspaceType space_type) {
auto to_hdf5 = [](auto _space_type) -> H5S_class_t {
switch (_space_type) {
case DataSpace::dataspace_scalar:
return H5S_SCALAR;
case DataSpace::dataspace_null:
return H5S_NULL;
default:
throw DataSpaceException(
"Invalid dataspace type: should be "
"dataspace_scalar or dataspace_null");
}
};
_hid = detail::h5s_create(to_hdf5(space_type));
}
inline DataSpace DataSpace::clone() const {
DataSpace res;
res._hid = detail::h5s_copy(_hid);
return res;
}
inline size_t DataSpace::getNumberDimensions() const {
return static_cast<size_t>(detail::h5s_get_simple_extent_ndims(_hid));
}
inline std::vector<size_t> DataSpace::getDimensions() const {
std::vector<hsize_t> dims(getNumberDimensions());
if (!dims.empty()) {
detail::h5s_get_simple_extent_dims(_hid, dims.data(), nullptr);
}
return details::to_vector_size_t(std::move(dims));
}
inline size_t DataSpace::getElementCount() const {
return static_cast<size_t>(detail::h5s_get_simple_extent_npoints(_hid));
}
inline std::vector<size_t> DataSpace::getMaxDimensions() const {
std::vector<hsize_t> maxdims(getNumberDimensions());
detail::h5s_get_simple_extent_dims(_hid, nullptr, maxdims.data());
std::replace(maxdims.begin(),
maxdims.end(),
H5S_UNLIMITED,
static_cast<hsize_t>(DataSpace::UNLIMITED));
return details::to_vector_size_t(maxdims);
}
template <typename T>
inline DataSpace DataSpace::From(const T& value) {
auto dims = details::inspector<T>::getDimensions(value);
return DataSpace(dims);
}
template <std::size_t N, std::size_t Width>
inline DataSpace DataSpace::FromCharArrayStrings(const char (&)[N][Width]) {
return DataSpace(N);
}
namespace details {
inline bool checkDimensions(const DataSpace& mem_space,
size_t min_dim_requested,
size_t max_dim_requested) {
return checkDimensions(mem_space.getDimensions(), min_dim_requested, max_dim_requested);
}
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,67 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <cstdlib>
#include <sstream>
#include "h5_wrapper.hpp"
#include "h5e_wrapper.hpp"
namespace HighFive {
struct HDF5ErrMapper {
template <typename ExceptionType>
static inline herr_t stackWalk(unsigned /* n */,
const H5E_error2_t* err_desc,
void* client_data) {
auto** e_iter = static_cast<ExceptionType**>(client_data);
const char* major_err = detail::nothrow::h5e_get_major(err_desc->maj_num);
const char* minor_err = detail::nothrow::h5e_get_minor(err_desc->min_num);
std::ostringstream oss;
oss << '(' << major_err << ") " << minor_err;
detail::nothrow::h5_free_memory((void*) major_err);
detail::nothrow::h5_free_memory((void*) minor_err);
auto* e = new ExceptionType(oss.str());
e->_err_major = err_desc->maj_num;
e->_err_minor = err_desc->min_num;
(*e_iter)->_next.reset(e);
*e_iter = e;
return 0;
}
template <typename ExceptionType>
[[noreturn]] static inline void ToException(const std::string& prefix_msg) {
hid_t err_stack = H5Eget_current_stack();
if (err_stack >= 0) {
ExceptionType e("");
ExceptionType* e_iter = &e;
detail::nothrow::h5e_walk2(err_stack,
H5E_WALK_UPWARD,
&HDF5ErrMapper::stackWalk<ExceptionType>,
(void*) &e_iter);
detail::nothrow::h5e_clear2(err_stack);
const char* next_err_msg = (e.nextException() != NULL) ? (e.nextException()->what())
: ("");
e.setErrorMsg(prefix_msg + " " + next_err_msg);
throw e;
}
// throw generic error, unrecognized error
throw ExceptionType(prefix_msg + ": Unknown HDF5 error");
}
};
} // namespace HighFive

View File

@ -0,0 +1,140 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include <H5Fpublic.h>
#include "../H5Utility.hpp"
#include "H5Utils.hpp"
#include "h5f_wrapper.hpp"
namespace HighFive {
namespace { // unnamed
// libhdf5 uses a preprocessor trick on their oflags
// we can not declare them constant without a mapper
inline unsigned convert_open_flag(File::AccessMode openFlags) {
unsigned res_open = 0;
if (any(openFlags & File::ReadOnly))
res_open |= H5F_ACC_RDONLY;
if (any(openFlags & File::ReadWrite))
res_open |= H5F_ACC_RDWR;
if (any(openFlags & File::Create))
res_open |= H5F_ACC_CREAT;
if (any(openFlags & File::Truncate))
res_open |= H5F_ACC_TRUNC;
if (any(openFlags & File::Excl))
res_open |= H5F_ACC_EXCL;
return res_open;
}
} // namespace
inline File::File(const std::string& filename,
AccessMode openFlags,
const FileAccessProps& fileAccessProps)
: File(filename, openFlags, FileCreateProps::Default(), fileAccessProps) {}
inline File::File(const std::string& filename,
AccessMode access_mode,
const FileCreateProps& fileCreateProps,
const FileAccessProps& fileAccessProps) {
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);
bool mustCreate = createMode > 0;
bool openOrCreate = (openFlags & H5F_ACC_CREAT) > 0;
// open is default. It's skipped only if flags require creation
// If open fails it will try create() if H5F_ACC_CREAT is set
if (!mustCreate) {
// Silence open errors if create is allowed
std::unique_ptr<SilenceHDF5> silencer;
if (openOrCreate) {
silencer = std::make_unique<SilenceHDF5>();
}
_hid = detail::nothrow::h5f_open(filename.c_str(), openMode, fileAccessProps.getId());
if (isValid()) {
return; // Done
}
if (openOrCreate) {
// Will attempt to create ensuring wont clobber any file
createMode = H5F_ACC_EXCL;
} else {
HDF5ErrMapper::ToException<FileException>(
std::string("Unable to open file " + filename));
}
}
auto fcpl = fileCreateProps.getId();
auto fapl = fileAccessProps.getId();
_hid = detail::h5f_create(filename.c_str(), createMode, fcpl, fapl);
}
inline const std::string& File::getName() const {
if (_filename.empty()) {
_filename = details::get_name([this](char* buffer, size_t length) {
return detail::h5f_get_name(getId(), buffer, length);
});
}
return _filename;
}
inline hsize_t File::getMetadataBlockSize() const {
auto fapl = getAccessPropertyList();
return MetadataBlockSize(fapl).getSize();
}
inline std::pair<H5F_libver_t, H5F_libver_t> File::getVersionBounds() const {
auto fapl = getAccessPropertyList();
auto fileVer = FileVersionBounds(fapl);
return fileVer.getVersion();
}
#if H5_VERSION_GE(1, 10, 1)
inline H5F_fspace_strategy_t File::getFileSpaceStrategy() const {
auto fcpl = getCreatePropertyList();
FileSpaceStrategy spaceStrategy(fcpl);
return spaceStrategy.getStrategy();
}
inline hsize_t File::getFileSpacePageSize() const {
auto fcpl = getCreatePropertyList();
if (getFileSpaceStrategy() != H5F_FSPACE_STRATEGY_PAGE) {
HDF5ErrMapper::ToException<FileException>(
std::string("Cannot obtain page size as paged allocation is not used."));
}
return FileSpacePageSize(fcpl).getPageSize();
}
#endif
inline void File::flush() {
detail::h5f_flush(_hid, H5F_SCOPE_GLOBAL);
}
inline size_t File::getFileSize() const {
hsize_t sizeValue = 0;
detail::h5f_get_filesize(_hid, &sizeValue);
return static_cast<size_t>(sizeValue);
}
inline size_t File::getFreeSpace() const {
return static_cast<size_t>(detail::h5f_get_freespace(_hid));
}
} // namespace HighFive

View File

@ -0,0 +1,10 @@
#pragma once
#ifndef HIGHFIVE_HAS_FRIEND_DECLARATIONS
#ifdef _MSC_VER
// This prevents a compiler bug on certain versions of MSVC.
// Known to fail: Toolset 141.
// See `CMakeLists.txt` for more information.
#define HIGHFIVE_HAS_FRIEND_DECLARATIONS 1
#endif
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#include "compute_total_size.hpp"
namespace HighFive {
template <typename T>
using unqualified_t = typename std::remove_const<typename std::remove_reference<T>::type>::type;
namespace details {
template <typename T>
struct type_helper;
template <typename T>
struct inspector;
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,524 @@
/*
* Copyright (c) 2022 Blue Brain Project
*
* 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)
*
*/
#pragma once
#include <type_traits>
#include <cstring>
#include <cassert>
#include <vector>
#include <array>
#include <string>
#include <numeric>
#include "../H5Reference.hpp"
#include "string_padding.hpp"
#include "H5Inspector_decl.hpp"
namespace HighFive {
namespace details {
inline bool checkDimensions(const std::vector<size_t>& dims,
size_t min_dim_requested,
size_t max_dim_requested) {
if (min_dim_requested <= dims.size() && dims.size() <= max_dim_requested) {
return true;
}
// Scalar values still support broadcasting
// into arrays with one element.
size_t n_elements = compute_total_size(dims);
return n_elements == 1 && min_dim_requested == 0;
}
} // namespace details
/*****
inspector<T> {
using type = T
// base_type is the base type inside c++ (e.g. std::vector<int> => int)
using base_type
// hdf5_type is the base read by hdf5 (c-type) (e.g. std::vector<std::string> => const char*)
using hdf5_type
// Is the inner type trivially copyable for optimisation
// If this value is true: data() is mandatory
// If this value is false: serialize, unserialize are mandatory
static constexpr bool is_trivially_copyable
// Is this type trivially nestable, i.e. is type[n] a contiguous
// array of `base_type[N]`?
static constexpr bool is_trivially_nestable
// Reading:
// Allocate the value following dims (should be recursive)
static void prepare(type& val, const std::vector<std::size_t> dims)
// Return a pointer of the first value of val (for reading)
static hdf5_type* data(type& val)
// Take a serialized vector 'in', some dims and copy value to val (for reading)
static void unserialize(const hdf5_type* in, const std::vector<size_t>&i, type& val)
// Writing:
// Return a point of the first value of val
static const hdf5_type* data(const type& val)
// Take a val and serialize it inside 'out'
static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* out)
// Return an array of dimensions of the space needed for writing val
static std::vector<size_t> getDimensions(const type& val)
}
*****/
namespace details {
template <typename T>
struct type_helper {
using type = unqualified_t<T>;
using base_type = unqualified_t<T>;
using hdf5_type = base_type;
static constexpr size_t ndim = 0;
static constexpr size_t min_ndim = ndim;
static constexpr size_t max_ndim = ndim;
static constexpr bool is_trivially_copyable = std::is_trivially_copyable<type>::value;
static constexpr bool is_trivially_nestable = is_trivially_copyable;
static size_t getRank(const type& /* val */) {
return ndim;
}
static std::vector<size_t> getDimensions(const type& /* val */) {
return {};
}
static void prepare(type& /* val */, const std::vector<size_t>& /* dims */) {}
static hdf5_type* data(type& val) {
static_assert(is_trivially_copyable, "The type is not trivially copyable");
return &val;
}
static const hdf5_type* data(const type& val) {
static_assert(is_trivially_copyable, "The type is not trivially copyable");
return &val;
}
static void serialize(const type& val, const std::vector<size_t>& /* dims*/, hdf5_type* m) {
static_assert(is_trivially_copyable, "The type is not trivially copyable");
*m = val;
}
static void unserialize(const hdf5_type* vec,
const std::vector<size_t>& /* dims */,
type& val) {
static_assert(is_trivially_copyable, "The type is not trivially copyable");
val = vec[0];
}
};
template <typename T>
struct inspector: type_helper<T> {};
enum class Boolean : int8_t {
HighFiveFalse = 0,
HighFiveTrue = 1,
};
template <>
struct inspector<bool>: type_helper<bool> {
using base_type = Boolean;
using hdf5_type = int8_t;
static constexpr bool is_trivially_copyable = false;
static constexpr bool is_trivially_nestable = false;
static hdf5_type* data(type& /* val */) {
throw DataSpaceException("A boolean cannot be read directly.");
}
static const hdf5_type* data(const type& /* val */) {
throw DataSpaceException("A boolean cannot be written directly.");
}
static void unserialize(const hdf5_type* vec,
const std::vector<size_t>& /* dims */,
type& val) {
val = vec[0] != 0;
}
static void serialize(const type& val, const std::vector<size_t>& /* dims*/, hdf5_type* m) {
*m = val ? 1 : 0;
}
};
template <>
struct inspector<std::string>: type_helper<std::string> {
using hdf5_type = const char*;
static hdf5_type* data(type& /* val */) {
throw DataSpaceException("A std::string cannot be read directly.");
}
static const hdf5_type* data(const type& /* val */) {
throw DataSpaceException("A std::string cannot be written directly.");
}
template <class It>
static void serialize(const type& val, const std::vector<size_t>& /* dims*/, It m) {
(*m).assign(val.data(), val.size(), StringPadding::NullTerminated);
}
template <class It>
static void unserialize(const It& vec, const std::vector<size_t>& /* dims */, type& val) {
const auto& view = *vec;
val.assign(view.data(), view.length());
}
};
template <>
struct inspector<Reference>: type_helper<Reference> {
using hdf5_type = hobj_ref_t;
static constexpr bool is_trivially_copyable = false;
static constexpr bool is_trivially_nestable = false;
static hdf5_type* data(type& /* val */) {
throw DataSpaceException("A Reference cannot be read directly.");
}
static const hdf5_type* data(const type& /* val */) {
throw DataSpaceException("A Reference cannot be written directly.");
}
static void serialize(const type& val, const std::vector<size_t>& /* dims*/, hdf5_type* m) {
hobj_ref_t ref;
val.create_ref(&ref);
*m = ref;
}
static void unserialize(const hdf5_type* vec,
const std::vector<size_t>& /* dims */,
type& val) {
val = type{vec[0]};
}
};
template <typename T>
struct inspector<std::vector<T>> {
using type = std::vector<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 = 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]);
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>& dims) {
val.resize(dims[0]);
std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
for (auto&& e: val) {
inspector<value_type>::prepare(e, next_dims);
}
}
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() + 1, dims.end());
size_t subsize = compute_total_size(subdims);
for (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> next_dims(dims.begin() + 1, dims.end());
size_t next_size = compute_total_size(next_dims);
for (size_t i = 0; i < dims[0]; ++i) {
inspector<value_type>::unserialize(vec_align + i * next_size, next_dims, val[i]);
}
}
};
template <>
struct inspector<std::vector<bool>> {
using type = std::vector<bool>;
using value_type = bool;
using base_type = Boolean;
using hdf5_type = uint8_t;
static constexpr size_t ndim = 1;
static constexpr size_t min_ndim = ndim;
static constexpr size_t max_ndim = ndim;
static constexpr bool is_trivially_copyable = false;
static constexpr bool is_trivially_nestable = false;
static size_t getRank(const type& /* val */) {
return ndim;
}
static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{val.size()};
return sizes;
}
static void prepare(type& val, const std::vector<size_t>& dims) {
if (dims.size() > 1) {
throw DataSpaceException("std::vector<bool> is only 1 dimension.");
}
val.resize(dims[0]);
}
static hdf5_type* data(type& /* val */) {
throw DataSpaceException("A std::vector<bool> cannot be read directly.");
}
static const hdf5_type* data(const type& /* val */) {
throw DataSpaceException("A std::vector<bool> cannot be written directly.");
}
static void serialize(const type& val, const std::vector<size_t>& /* dims*/, hdf5_type* m) {
for (size_t i = 0; i < val.size(); ++i) {
m[i] = val[i] ? 1 : 0;
}
}
static void unserialize(const hdf5_type* vec_align,
const std::vector<size_t>& dims,
type& val) {
for (size_t i = 0; i < dims[0]; ++i) {
val[i] = vec_align[i] != 0;
}
}
};
template <typename T, size_t N>
struct inspector<std::array<T, N>> {
using type = std::array<T, N>;
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 = (sizeof(type) == N * sizeof(T)) &&
is_trivially_copyable;
static size_t getRank(const type& val) {
return ndim + inspector<value_type>::getRank(val[0]);
}
static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{N};
auto s = inspector<value_type>::getDimensions(val[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
return sizes;
}
static void prepare(type& val, const std::vector<size_t>& dims) {
if (dims[0] > N) {
std::ostringstream os;
os << "Size of std::array (" << N << ") is too small for dims (" << dims[0] << ").";
throw DataSpaceException(os.str());
}
std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
for (auto&& e: val) {
inspector<value_type>::prepare(e, next_dims);
}
}
static hdf5_type* data(type& val) {
return inspector<value_type>::data(val[0]);
}
static const hdf5_type* data(const type& val) {
return inspector<value_type>::data(val[0]);
}
template <class It>
static void serialize(const type& val, const std::vector<size_t>& dims, It m) {
auto subdims = std::vector<size_t>(dims.begin() + 1, dims.end());
size_t subsize = compute_total_size(subdims);
for (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) {
if (dims[0] != N) {
std::ostringstream os;
os << "Impossible to pair DataSet with " << dims[0] << " elements into an array with "
<< N << " elements.";
throw DataSpaceException(os.str());
}
std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
size_t next_size = compute_total_size(next_dims);
for (size_t i = 0; i < val.size(); ++i) {
inspector<value_type>::unserialize(vec_align + i * next_size, next_dims, val[i]);
}
}
};
// Cannot be use for reading
template <typename T>
struct inspector<T*> {
using type = 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 = 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 != nullptr) {
return ndim + inspector<value_type>::getRank(val[0]);
} else {
return min_ndim;
}
}
static std::vector<size_t> getDimensions(const type& /* val */) {
throw DataSpaceException("Not possible to have size of a T*");
}
static const hdf5_type* data(const type& val) {
return reinterpret_cast<const hdf5_type*>(val);
}
/* it works because there is only T[][][] currently
we will fix it one day */
static void serialize(const type& /* val */,
const std::vector<size_t>& /* dims*/,
hdf5_type* /* m */) {
throw DataSpaceException("Not possible to serialize a T*");
}
};
// Cannot be use for reading
template <typename T, size_t N>
struct inspector<T[N]> {
using type = T[N];
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 = is_trivially_copyable;
static void prepare(type& val, const std::vector<size_t>& dims) {
if (dims.empty()) {
throw DataSpaceException("Invalid 'dims', must be at least 1 dimensional.");
}
if (dims[0] != N) {
throw DataSpaceException("Dimensions mismatch.");
}
std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
for (size_t i = 0; i < N; ++i) {
inspector<value_type>::prepare(val[i], next_dims);
}
}
static size_t getRank(const type& val) {
return ndim + inspector<value_type>::getRank(val[0]);
}
static std::vector<size_t> getDimensions(const type& val) {
std::vector<size_t> sizes{N};
auto s = inspector<value_type>::getDimensions(val[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
return sizes;
}
static const hdf5_type* data(const type& val) {
return inspector<value_type>::data(val[0]);
}
static hdf5_type* data(type& val) {
return inspector<value_type>::data(val[0]);
}
/* it works because there is only T[][][] currently
we will fix it one day */
static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* m) {
auto subdims = std::vector<size_t>(dims.begin() + 1, dims.end());
size_t subsize = compute_total_size(subdims);
for (size_t i = 0; i < N; ++i) {
inspector<value_type>::serialize(val[i], subdims, m + i * subsize);
}
}
};
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,54 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <exception>
#include <string>
#include <vector>
#include <H5Ipublic.h>
namespace HighFive {
namespace details {
// iterator for H5 iterate
struct HighFiveIterateData {
explicit HighFiveIterateData(std::vector<std::string>& my_names)
: names(my_names)
, err(nullptr) {}
std::vector<std::string>& names;
std::exception* err;
inline void throwIfError() const {
if (err != nullptr) {
throw *err;
}
}
};
template <typename InfoType>
inline herr_t internal_high_five_iterate(hid_t /*id*/,
const char* name,
const InfoType* /*info*/,
void* op_data) {
auto* data = static_cast<HighFiveIterateData*>(op_data);
try {
data->names.emplace_back(name);
return 0;
} catch (...) {
data->err = new ObjectException("Exception during H5Iterate, abort listing");
}
return -1;
}
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,234 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include "../H5PropertyList.hpp"
#include "H5_definitions.hpp"
#include "H5Converter_misc.hpp"
namespace HighFive {
enum class IndexType : std::underlying_type<H5_index_t>::type {
NAME = H5_INDEX_NAME,
CRT_ORDER = H5_INDEX_CRT_ORDER,
};
///
/// \brief NodeTraits: Base class for Group and File
///
template <typename Derivate>
class NodeTraits {
public:
///
/// \brief createDataSet Create a new dataset in the current file of
/// datatype type and of size space
/// \param dataset_name identifier of the dataset
/// \param space Associated DataSpace, see \ref DataSpace for more information
/// \param type Type of Data
/// \param createProps A property list with data set creation properties
/// \param accessProps A property list with data set access properties
/// \param parents Create intermediate groups if needed. Default: true.
/// \return DataSet Object
DataSet createDataSet(const std::string& dataset_name,
const DataSpace& space,
const DataType& type,
const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
bool parents = true);
///
/// \brief createDataSet create a new dataset in the current file with a
/// size specified by space
/// \param dataset_name identifier of the dataset
/// \param space Associated DataSpace, see \ref DataSpace for more information
/// \param createProps A property list with data set creation properties
/// \param accessProps A property list with data set access properties
/// \param parents Create intermediate groups if needed. Default: true.
/// \return DataSet Object
template <typename T>
DataSet createDataSet(const std::string& dataset_name,
const DataSpace& space,
const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
bool parents = true);
///
/// \brief createDataSet create a new dataset in the current file and
/// write to it, inferring the DataSpace from the data.
/// \param dataset_name identifier of the dataset
/// \param data Associated data, must support DataSpace::From, see
/// \ref DataSpace for more information
/// \param createProps A property list with data set creation properties
/// \param accessProps A property list with data set access properties
/// \param parents Create intermediate groups if needed. Default: true.
/// \return DataSet Object
template <typename T>
DataSet createDataSet(const std::string& dataset_name,
const T& data,
const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
bool parents = true);
///
/// \brief get an existing dataset in the current file
/// \param dataset_name
/// \param accessProps property list to configure dataset chunk cache
/// \return return the named dataset, or throw exception if not found
DataSet getDataSet(const std::string& dataset_name,
const DataSetAccessProps& accessProps = DataSetAccessProps::Default()) const;
///
/// \brief create a new group, and eventually intermediate groups
/// \param group_name
/// \param parents Create intermediate groups if needed. Default: true.
/// \return the group object
Group createGroup(const std::string& group_name, bool parents = true);
///
/// \brief create a new group, and eventually intermediate groups
/// \param group_name
/// \param createProps A property list with group creation properties
/// \param parents Create intermediate groups if needed. Default: true.
/// \return the group object
Group createGroup(const std::string& group_name,
const GroupCreateProps& createProps,
bool parents = true);
///
/// \brief open an existing group with the name group_name
/// \param group_name
/// \return the group object
Group getGroup(const std::string& group_name) const;
///
/// \brief open a commited datatype with the name type_name
/// \param type_name
/// \return the datatype object
DataType getDataType(
const std::string& type_name,
const DataTypeAccessProps& accessProps = DataTypeAccessProps::Default()) const;
///
/// \brief return the number of leaf objects of the node / group
/// \return number of leaf objects
size_t getNumberObjects() const;
///
/// \brief return the name of the object with the given index
/// \return the name of the object
std::string getObjectName(size_t index) const;
///
/// \brief moves an object and its content within an HDF5 file.
/// \param src_path relative path of the object to current File/Group
/// \param dst_path new relative path of the object to current File/Group
/// \param parents Create intermediate groups if needed. Default: true.
/// \return boolean that is true if the move was successful
bool rename(const std::string& src_path,
const std::string& dst_path,
bool parents = true) const;
///
/// \brief list all leaf objects name of the node / group
/// \param idx_type tell if the list should be ordered by Name or CreationOrderTime.
/// CreationOrderTime can be use only if the file/group has been created with
/// the HighFive::LinkCreationTime property.
/// \return number of leaf objects
std::vector<std::string> listObjectNames(IndexType idx_type = IndexType::NAME) const;
///
/// \brief check a dataset or group exists in the current node / group
/// \param node_path dataset/group name to check
/// \return true if a dataset/group with the associated name exists, or false
bool exist(const std::string& node_path) const;
///
/// \brief unlink the given dataset or group
/// \param node_path dataset/group name to unlink
void unlink(const std::string& node_path) const;
///
/// \brief Returns the kind of link of the given name (soft, hard...)
/// \param node_path The entry to check, path relative to the current group
LinkType getLinkType(const std::string& node_path) const;
///
/// \brief A shorthand to get the kind of object pointed to (group, dataset, type...)
/// \param node_path The entry to check, path relative to the current group
ObjectType getObjectType(const std::string& node_path) const;
///
/// \brief A shorthand to create softlink to any object which provides `getPath`
/// The link will be created with default properties along with required parent groups
template <typename T, typename = decltype(&T::getPath)>
void createSoftLink(const std::string& linkName, const T& obj) {
static_assert(!std::is_same<T, Attribute>::value,
"hdf5 doesn't support soft links to Attributes");
createSoftLink(linkName, obj.getPath());
}
///
/// \brief Creates softlinks
/// \param link_name The name of the link
/// \param obj_path The target object path
/// \param linkCreateProps A Link_Create property list. Notice "parents=true" overrides
/// \param linkAccessProps The Link_Access property list
/// \param parents Whether parent groups should be created: Default: true
void createSoftLink(const std::string& link_name,
const std::string& obj_path,
LinkCreateProps linkCreateProps = LinkCreateProps(),
const LinkAccessProps& linkAccessProps = LinkAccessProps(),
bool parents = true);
void createExternalLink(const std::string& link_name,
const std::string& h5_file,
const std::string& obj_path,
LinkCreateProps linkCreateProps = LinkCreateProps(),
const LinkAccessProps& linkAccessProps = LinkAccessProps(),
bool parents = true);
///
/// \brief Creates hardlinks
/// \param link_name The name of the link
/// \param target_obj The target object
/// \param linkCreateProps A Link_Create property list. Notice "parents=true" overrides
/// \param linkAccessProps The Link_Access property list
/// \param parents Whether parent groups should be created: Default: true
template <typename T, typename = decltype(&T::getPath)>
void createHardLink(const std::string& link_name,
const T& target_obj,
LinkCreateProps linkCreateProps = LinkCreateProps(),
const LinkAccessProps& linkAccessProps = LinkAccessProps(),
bool parents = true);
private:
using derivate_type = Derivate;
// A wrapper over the low-level H5Lexist
// It makes behavior consistent among versions and by default transforms
// errors to exceptions
bool _exist(const std::string& node_path, bool raise_errors = true) const;
};
///
/// \brief The possible types of group entries (link concept)
///
enum class LinkType {
Hard,
Soft,
External,
Other // Reserved or User-defined
};
} // namespace HighFive

View File

@ -0,0 +1,322 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <string>
#include <vector>
#include <H5Apublic.h>
#include <H5Fpublic.h>
#include <H5Ppublic.h>
#include <H5Tpublic.h>
#include "../H5DataSet.hpp"
#include "../H5Group.hpp"
#include "../H5Selection.hpp"
#include "../H5Utility.hpp"
#include "H5DataSet_misc.hpp"
#include "H5Iterables_misc.hpp"
#include "H5Selection_misc.hpp"
#include "H5Slice_traits_misc.hpp"
#include "h5l_wrapper.hpp"
#include "h5g_wrapper.hpp"
#include "h5o_wrapper.hpp"
namespace HighFive {
template <typename Derivate>
inline DataSet NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
const DataSpace& space,
const DataType& dtype,
const DataSetCreateProps& createProps,
const DataSetAccessProps& accessProps,
bool parents) {
LinkCreateProps lcpl;
lcpl.add(CreateIntermediateGroup(parents));
return DataSet(detail::h5d_create2(static_cast<Derivate*>(this)->getId(),
dataset_name.c_str(),
dtype.getId(),
space.getId(),
lcpl.getId(),
createProps.getId(),
accessProps.getId()));
}
template <typename Derivate>
template <typename T>
inline DataSet NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
const DataSpace& space,
const DataSetCreateProps& createProps,
const DataSetAccessProps& accessProps,
bool parents) {
return createDataSet(
dataset_name, space, create_and_check_datatype<T>(), createProps, accessProps, parents);
}
template <typename Derivate>
template <typename T>
inline DataSet NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
const T& data,
const DataSetCreateProps& createProps,
const DataSetAccessProps& accessProps,
bool parents) {
DataSet ds =
createDataSet(dataset_name,
DataSpace::From(data),
create_and_check_datatype<typename details::inspector<T>::base_type>(),
createProps,
accessProps,
parents);
ds.write(data);
return ds;
}
template <typename Derivate>
inline DataSet NodeTraits<Derivate>::getDataSet(const std::string& dataset_name,
const DataSetAccessProps& accessProps) const {
return DataSet(detail::h5d_open2(static_cast<const Derivate*>(this)->getId(),
dataset_name.c_str(),
accessProps.getId()));
}
template <typename Derivate>
inline Group NodeTraits<Derivate>::createGroup(const std::string& group_name, bool parents) {
LinkCreateProps lcpl;
lcpl.add(CreateIntermediateGroup(parents));
return detail::make_group(detail::h5g_create2(static_cast<Derivate*>(this)->getId(),
group_name.c_str(),
lcpl.getId(),
H5P_DEFAULT,
H5P_DEFAULT));
}
template <typename Derivate>
inline Group NodeTraits<Derivate>::createGroup(const std::string& group_name,
const GroupCreateProps& createProps,
bool parents) {
LinkCreateProps lcpl;
lcpl.add(CreateIntermediateGroup(parents));
return detail::make_group(detail::h5g_create2(static_cast<Derivate*>(this)->getId(),
group_name.c_str(),
lcpl.getId(),
createProps.getId(),
H5P_DEFAULT));
}
template <typename Derivate>
inline Group NodeTraits<Derivate>::getGroup(const std::string& group_name) const {
return detail::make_group(detail::h5g_open2(static_cast<const Derivate*>(this)->getId(),
group_name.c_str(),
H5P_DEFAULT));
}
template <typename Derivate>
inline DataType NodeTraits<Derivate>::getDataType(const std::string& type_name,
const DataTypeAccessProps& accessProps) const {
return DataType(detail::h5t_open2(static_cast<const Derivate*>(this)->getId(),
type_name.c_str(),
accessProps.getId()));
}
template <typename Derivate>
inline size_t NodeTraits<Derivate>::getNumberObjects() const {
hsize_t res;
detail::h5g_get_num_objs(static_cast<const Derivate*>(this)->getId(), &res);
return static_cast<size_t>(res);
}
template <typename Derivate>
inline std::string NodeTraits<Derivate>::getObjectName(size_t index) const {
return details::get_name([&](char* buffer, size_t length) {
return detail::h5l_get_name_by_idx(static_cast<const Derivate*>(this)->getId(),
".",
H5_INDEX_NAME,
H5_ITER_INC,
index,
buffer,
length,
H5P_DEFAULT);
});
}
template <typename Derivate>
inline bool NodeTraits<Derivate>::rename(const std::string& src_path,
const std::string& dst_path,
bool parents) const {
LinkCreateProps lcpl;
lcpl.add(CreateIntermediateGroup(parents));
herr_t err = detail::h5l_move(static_cast<const Derivate*>(this)->getId(),
src_path.c_str(),
static_cast<const Derivate*>(this)->getId(),
dst_path.c_str(),
lcpl.getId(),
H5P_DEFAULT);
return err >= 0;
}
template <typename Derivate>
inline std::vector<std::string> NodeTraits<Derivate>::listObjectNames(IndexType idx_type) const {
std::vector<std::string> names;
details::HighFiveIterateData iterateData(names);
size_t num_objs = getNumberObjects();
names.reserve(num_objs);
detail::h5l_iterate(static_cast<const Derivate*>(this)->getId(),
static_cast<H5_index_t>(idx_type),
H5_ITER_INC,
NULL,
&details::internal_high_five_iterate<H5L_info_t>,
static_cast<void*>(&iterateData));
return names;
}
template <typename Derivate>
inline bool NodeTraits<Derivate>::_exist(const std::string& node_path, bool raise_errors) const {
SilenceHDF5 silencer{};
const auto val = detail::nothrow::h5l_exists(static_cast<const Derivate*>(this)->getId(),
node_path.c_str(),
H5P_DEFAULT);
if (val < 0) {
if (raise_errors) {
HDF5ErrMapper::ToException<GroupException>("Invalid link for exist()");
} else {
return false;
}
}
// The root path always exists, but H5Lexists return 0 or 1
// depending of the version of HDF5, so always return true for it
// We had to call H5Lexists anyway to check that there are no errors
return (node_path == "/") ? true : (val > 0);
}
template <typename Derivate>
inline bool NodeTraits<Derivate>::exist(const std::string& node_path) const {
// When there are slashes, first check everything is fine
// so that subsequent errors are only due to missing intermediate groups
if (node_path.find('/') != std::string::npos) {
_exist("/"); // Shall not throw under normal circumstances
// Unless "/" (already checked), verify path exists (not throwing errors)
return (node_path == "/") ? true : _exist(node_path, false);
}
return _exist(node_path);
}
template <typename Derivate>
inline void NodeTraits<Derivate>::unlink(const std::string& node_path) const {
detail::h5l_delete(static_cast<const Derivate*>(this)->getId(), node_path.c_str(), H5P_DEFAULT);
}
// convert internal link types to enum class.
// This function is internal, so H5L_TYPE_ERROR shall be handled in the calling context
static inline LinkType _convert_link_type(const H5L_type_t& ltype) noexcept {
switch (ltype) {
case H5L_TYPE_HARD:
return LinkType::Hard;
case H5L_TYPE_SOFT:
return LinkType::Soft;
case H5L_TYPE_EXTERNAL:
return LinkType::External;
default:
// Other link types are possible but are considered strange to HighFive.
// see https://support.hdfgroup.org/HDF5/doc/RM/H5L/H5Lregister.htm
return LinkType::Other;
}
}
template <typename Derivate>
inline LinkType NodeTraits<Derivate>::getLinkType(const std::string& node_path) const {
H5L_info_t linkinfo;
detail::h5l_get_info(static_cast<const Derivate*>(this)->getId(),
node_path.c_str(),
&linkinfo,
H5P_DEFAULT);
if (linkinfo.type == H5L_TYPE_ERROR) {
HDF5ErrMapper::ToException<GroupException>(std::string("Link type of \"") + node_path +
"\" is H5L_TYPE_ERROR");
}
return _convert_link_type(linkinfo.type);
}
template <typename Derivate>
inline ObjectType NodeTraits<Derivate>::getObjectType(const std::string& node_path) const {
const auto id = detail::h5o_open(static_cast<const Derivate*>(this)->getId(),
node_path.c_str(),
H5P_DEFAULT);
auto object_type = _convert_object_type(detail::h5i_get_type(id));
detail::h5o_close(id);
return object_type;
}
template <typename Derivate>
inline void NodeTraits<Derivate>::createSoftLink(const std::string& link_name,
const std::string& obj_path,
LinkCreateProps linkCreateProps,
const LinkAccessProps& linkAccessProps,
const bool parents) {
if (parents) {
linkCreateProps.add(CreateIntermediateGroup{});
}
detail::h5l_create_soft(obj_path.c_str(),
static_cast<const Derivate*>(this)->getId(),
link_name.c_str(),
linkCreateProps.getId(),
linkAccessProps.getId());
}
template <typename Derivate>
inline void NodeTraits<Derivate>::createExternalLink(const std::string& link_name,
const std::string& h5_file,
const std::string& obj_path,
LinkCreateProps linkCreateProps,
const LinkAccessProps& linkAccessProps,
const bool parents) {
if (parents) {
linkCreateProps.add(CreateIntermediateGroup{});
}
detail::h5l_create_external(h5_file.c_str(),
obj_path.c_str(),
static_cast<const Derivate*>(this)->getId(),
link_name.c_str(),
linkCreateProps.getId(),
linkAccessProps.getId());
}
template <typename Derivate>
template <typename T, typename>
inline void NodeTraits<Derivate>::createHardLink(const std::string& link_name,
const T& target_obj,
LinkCreateProps linkCreateProps,
const LinkAccessProps& linkAccessProps,
const bool parents) {
static_assert(!std::is_same<T, Attribute>::value,
"hdf5 doesn't support hard links to Attributes");
if (parents) {
linkCreateProps.add(CreateIntermediateGroup{});
}
detail::h5l_create_hard(target_obj.getId(),
".",
static_cast<const Derivate*>(this)->getId(),
link_name.c_str(),
linkCreateProps.getId(),
linkAccessProps.getId());
}
} // namespace HighFive

View File

@ -0,0 +1,128 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <iostream>
#include "../H5Exception.hpp"
#include "../H5Utility.hpp"
#include "h5i_wrapper.hpp"
#include "h5o_wrapper.hpp"
namespace HighFive {
inline Object::Object()
: _hid(H5I_INVALID_HID) {}
inline Object::Object(hid_t hid) noexcept
: _hid(hid) {}
inline Object::Object(const Object& other)
: _hid(other._hid) {
if (other.isValid()) {
detail::h5i_inc_ref(_hid);
}
}
inline Object::Object(Object&& other) noexcept
: _hid(other._hid) {
other._hid = H5I_INVALID_HID;
}
inline Object& Object::operator=(Object&& other) {
if (this != &other) {
if ((*this).isValid()) {
detail::h5i_dec_ref(_hid);
}
_hid = other._hid;
other._hid = H5I_INVALID_HID;
}
return *this;
}
inline Object& Object::operator=(const Object& other) {
if (this != &other) {
if ((*this).isValid()) {
detail::h5i_dec_ref(_hid);
}
_hid = other._hid;
if (other.isValid()) {
detail::h5i_inc_ref(_hid);
}
}
return *this;
}
inline Object::~Object() {
if (isValid()) {
if (detail::nothrow::h5i_dec_ref(_hid) < 0) {
HIGHFIVE_LOG_ERROR("Failed to decrease reference count of HID");
}
}
}
inline bool Object::isValid() const noexcept {
return (_hid > 0) && (detail::nothrow::h5i_is_valid(_hid) > 0);
}
inline hid_t Object::getId() const noexcept {
return _hid;
}
static inline ObjectType _convert_object_type(const H5I_type_t& h5type) {
switch (h5type) {
case H5I_FILE:
return ObjectType::File;
case H5I_GROUP:
return ObjectType::Group;
case H5I_DATATYPE:
return ObjectType::UserDataType;
case H5I_DATASPACE:
return ObjectType::DataSpace;
case H5I_DATASET:
return ObjectType::Dataset;
case H5I_ATTR:
return ObjectType::Attribute;
default:
return ObjectType::Other;
}
}
inline ObjectType Object::getType() const {
// H5Iget_type is a very lightweight func which extracts the type from the id
return _convert_object_type(detail::h5i_get_type(_hid));
}
inline ObjectInfo Object::getInfo() const {
return ObjectInfo(*this);
}
inline haddr_t Object::getAddress() const {
detail::h5o_info1_t raw_info;
detail::h5o_get_info1(_hid, &raw_info);
return raw_info.addr;
}
inline ObjectInfo::ObjectInfo(const Object& obj) {
detail::h5o_get_info1(obj.getId(), &raw_info);
}
inline size_t ObjectInfo::getRefCount() const noexcept {
return raw_info.rc;
}
inline time_t ObjectInfo::getCreationTime() const noexcept {
return raw_info.btime;
}
inline time_t ObjectInfo::getModificationTime() const noexcept {
return raw_info.mtime;
}
} // namespace HighFive

View File

@ -0,0 +1,34 @@
/*
* Copyright (c), 2020, EPFL - Blue Brain Project
*
* 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)
*
*/
#pragma once
#include "H5_definitions.hpp"
namespace HighFive {
template <typename Derivate>
class PathTraits {
public:
PathTraits();
///
/// \brief return the path to the current object
/// \return the path to the object
std::string getPath() const;
///
/// \brief Return a reference to the File object this object belongs
/// \return the File object ref
File& getFile() const;
private:
std::shared_ptr<File> _file_obj; // keep a ref to file so we keep its ref count > 0
};
} // namespace HighFive

View File

@ -0,0 +1,46 @@
/*
* Copyright (c), 2020, EPFL - Blue Brain Project
*
* 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)
*
*/
#pragma once
#include <H5Ipublic.h>
#include "H5Utils.hpp"
#include "H5Path_traits.hpp"
namespace HighFive {
template <typename Derivate>
inline PathTraits<Derivate>::PathTraits() {
static_assert(std::is_same<Derivate, Group>::value || std::is_same<Derivate, DataSet>::value ||
std::is_same<Derivate, Attribute>::value,
"PathTraits can only be applied to Group, DataSet and Attribute");
const auto& obj = static_cast<const Derivate&>(*this);
if (obj.isValid()) {
const hid_t file_id = detail::h5i_get_file_id<PropertyException>(obj.getId());
_file_obj.reset(new File(file_id));
}
}
template <typename Derivate>
inline std::string PathTraits<Derivate>::getPath() const {
return details::get_name([this](char* buffer, size_t length) {
return detail::h5i_get_name(static_cast<const Derivate&>(*this).getId(), buffer, length);
});
}
template <typename Derivate>
inline File& PathTraits<Derivate>::getFile() const {
const auto& obj = static_cast<const Derivate&>(*this);
if (!obj.isValid()) {
throw ObjectException("Invalid call to `PathTraits::getFile` for invalid object");
}
return *_file_obj;
}
} // namespace HighFive

View File

@ -0,0 +1,492 @@
/*
* Copyright (c), 2017-2018, Adrien Devresse <adrien.devresse@epfl.ch>
* Juan Hernando <juan.hernando@epfl.ch>
* 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)
*
*/
#pragma once
#include "h5p_wrapper.hpp"
namespace HighFive {
namespace {
inline hid_t convert_plist_type(PropertyType propertyType) {
// The HP5_XXX are macros with function calls so we can't assign
// them as the enum values
switch (propertyType) {
case PropertyType::FILE_CREATE:
return H5P_FILE_CREATE;
case PropertyType::FILE_ACCESS:
return H5P_FILE_ACCESS;
case PropertyType::DATASET_CREATE:
return H5P_DATASET_CREATE;
case PropertyType::DATASET_ACCESS:
return H5P_DATASET_ACCESS;
case PropertyType::DATASET_XFER:
return H5P_DATASET_XFER;
case PropertyType::GROUP_CREATE:
return H5P_GROUP_CREATE;
case PropertyType::GROUP_ACCESS:
return H5P_GROUP_ACCESS;
case PropertyType::DATATYPE_CREATE:
return H5P_DATATYPE_CREATE;
case PropertyType::DATATYPE_ACCESS:
return H5P_DATATYPE_ACCESS;
case PropertyType::STRING_CREATE:
return H5P_STRING_CREATE;
case PropertyType::ATTRIBUTE_CREATE:
return H5P_ATTRIBUTE_CREATE;
case PropertyType::LINK_CREATE:
return H5P_LINK_CREATE;
case PropertyType::LINK_ACCESS:
return H5P_LINK_ACCESS;
default:
HDF5ErrMapper::ToException<PropertyException>("Unsupported property list type");
}
}
} // namespace
inline PropertyListBase::PropertyListBase() noexcept
: Object(H5P_DEFAULT) {}
template <PropertyType T>
inline void PropertyList<T>::_initializeIfNeeded() {
if (_hid != H5P_DEFAULT) {
return;
}
_hid = detail::h5p_create(convert_plist_type(T));
}
template <PropertyType T>
template <typename P>
inline void PropertyList<T>::add(const P& property) {
_initializeIfNeeded();
property.apply(_hid);
}
template <PropertyType T>
template <typename F, typename... Args>
inline void RawPropertyList<T>::add(const F& funct, const Args&... args) {
this->_initializeIfNeeded();
if (funct(this->_hid, args...) < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting raw hdf5 property.");
}
}
// Specific options to be added to Property Lists
#if H5_VERSION_GE(1, 10, 1)
inline FileSpaceStrategy::FileSpaceStrategy(H5F_fspace_strategy_t strategy,
hbool_t persist,
hsize_t threshold)
: _strategy(strategy)
, _persist(persist)
, _threshold(threshold) {}
inline FileSpaceStrategy::FileSpaceStrategy(const FileCreateProps& fcpl) {
detail::h5p_get_file_space_strategy(fcpl.getId(), &_strategy, &_persist, &_threshold);
}
inline void FileSpaceStrategy::apply(const hid_t list) const {
detail::h5p_set_file_space_strategy(list, _strategy, _persist, _threshold);
}
inline H5F_fspace_strategy_t FileSpaceStrategy::getStrategy() const {
return _strategy;
}
inline hbool_t FileSpaceStrategy::getPersist() const {
return _persist;
}
inline hsize_t FileSpaceStrategy::getThreshold() const {
return _threshold;
}
inline FileSpacePageSize::FileSpacePageSize(hsize_t page_size)
: _page_size(page_size) {}
inline void FileSpacePageSize::apply(const hid_t list) const {
detail::h5p_set_file_space_page_size(list, _page_size);
}
inline FileSpacePageSize::FileSpacePageSize(const FileCreateProps& fcpl) {
detail::h5p_get_file_space_page_size(fcpl.getId(), &_page_size);
}
inline hsize_t FileSpacePageSize::getPageSize() const {
return _page_size;
}
#ifndef H5_HAVE_PARALLEL
inline PageBufferSize::PageBufferSize(size_t page_buffer_size,
unsigned min_meta_percent,
unsigned min_raw_percent)
: _page_buffer_size(page_buffer_size)
, _min_meta(min_meta_percent)
, _min_raw(min_raw_percent) {}
inline PageBufferSize::PageBufferSize(const FileAccessProps& fapl) {
detail::h5p_get_page_buffer_size(fapl.getId(), &_page_buffer_size, &_min_meta, &_min_raw);
}
inline void PageBufferSize::apply(const hid_t list) const {
detail::h5p_set_page_buffer_size(list, _page_buffer_size, _min_meta, _min_raw);
}
inline size_t PageBufferSize::getPageBufferSize() const {
return _page_buffer_size;
}
inline unsigned PageBufferSize::getMinMetaPercent() const {
return _min_meta;
}
inline unsigned PageBufferSize::getMinRawPercent() const {
return _min_raw;
}
#endif
#endif
#ifdef H5_HAVE_PARALLEL
inline MPIOFileAccess::MPIOFileAccess(MPI_Comm comm, MPI_Info info)
: _comm(comm)
, _info(info) {}
inline void MPIOFileAccess::apply(const hid_t list) const {
detail::h5p_set_fapl_mpio(list, _comm, _info);
}
#if H5_VERSION_GE(1, 10, 0)
inline void MPIOCollectiveMetadata::apply(const hid_t plist) const {
auto read = MPIOCollectiveMetadataRead{collective_read_};
auto write = MPIOCollectiveMetadataWrite{collective_write_};
read.apply(plist);
write.apply(plist);
}
inline MPIOCollectiveMetadata::MPIOCollectiveMetadata(bool collective)
: collective_read_(collective)
, collective_write_(collective) {}
inline MPIOCollectiveMetadata::MPIOCollectiveMetadata(const FileAccessProps& plist)
: collective_read_(MPIOCollectiveMetadataRead(plist).isCollective())
, collective_write_(MPIOCollectiveMetadataWrite(plist).isCollective()) {}
inline bool MPIOCollectiveMetadata::isCollectiveRead() const {
return collective_read_;
}
inline bool MPIOCollectiveMetadata::isCollectiveWrite() const {
return collective_write_;
}
inline void MPIOCollectiveMetadataRead::apply(const hid_t plist) const {
detail::h5p_set_all_coll_metadata_ops(plist, collective_);
}
inline bool MPIOCollectiveMetadataRead::isCollective() const {
return collective_;
}
inline MPIOCollectiveMetadataRead::MPIOCollectiveMetadataRead(const FileAccessProps& plist) {
detail::h5p_get_all_coll_metadata_ops(plist.getId(), &collective_);
}
inline MPIOCollectiveMetadataRead::MPIOCollectiveMetadataRead(bool collective)
: collective_(collective) {}
inline void MPIOCollectiveMetadataWrite::apply(const hid_t plist) const {
detail::h5p_set_coll_metadata_write(plist, collective_);
}
inline bool MPIOCollectiveMetadataWrite::isCollective() const {
return collective_;
}
inline MPIOCollectiveMetadataWrite::MPIOCollectiveMetadataWrite(const FileAccessProps& plist) {
detail::h5p_get_coll_metadata_write(plist.getId(), &collective_);
}
inline MPIOCollectiveMetadataWrite::MPIOCollectiveMetadataWrite(bool collective)
: collective_(collective) {}
#endif
#endif
inline FileVersionBounds::FileVersionBounds(H5F_libver_t low, H5F_libver_t high)
: _low(low)
, _high(high) {}
inline FileVersionBounds::FileVersionBounds(const FileAccessProps& fapl) {
detail::h5p_get_libver_bounds(fapl.getId(), &_low, &_high);
}
inline std::pair<H5F_libver_t, H5F_libver_t> FileVersionBounds::getVersion() const {
return std::make_pair(_low, _high);
}
inline void FileVersionBounds::apply(const hid_t list) const {
detail::h5p_set_libver_bounds(list, _low, _high);
}
inline MetadataBlockSize::MetadataBlockSize(hsize_t size)
: _size(size) {}
inline MetadataBlockSize::MetadataBlockSize(const FileAccessProps& fapl) {
detail::h5p_get_meta_block_size(fapl.getId(), &_size);
}
inline void MetadataBlockSize::apply(const hid_t list) const {
detail::h5p_set_meta_block_size(list, _size);
}
inline hsize_t MetadataBlockSize::getSize() const {
return _size;
}
inline void EstimatedLinkInfo::apply(const hid_t hid) const {
detail::h5p_set_est_link_info(hid, _entries, _length);
}
inline EstimatedLinkInfo::EstimatedLinkInfo(unsigned entries, unsigned length)
: _entries(entries)
, _length(length) {}
inline EstimatedLinkInfo::EstimatedLinkInfo(const GroupCreateProps& gcpl) {
detail::h5p_get_est_link_info(gcpl.getId(), &_entries, &_length);
}
inline unsigned EstimatedLinkInfo::getEntries() const {
return _entries;
}
inline unsigned EstimatedLinkInfo::getNameLength() const {
return _length;
}
inline void Chunking::apply(const hid_t hid) const {
detail::h5p_set_chunk(hid, static_cast<int>(_dims.size()), _dims.data());
}
inline Chunking::Chunking(const std::vector<hsize_t>& dims)
: _dims(dims) {}
inline Chunking::Chunking(const std::initializer_list<hsize_t>& items)
: Chunking(std::vector<hsize_t>{items}) {}
inline Chunking::Chunking(DataSetCreateProps& plist, size_t max_dims)
: _dims(max_dims + 1) {
auto n_loaded =
detail::h5p_get_chunk(plist.getId(), static_cast<int>(_dims.size()), _dims.data());
if (n_loaded >= static_cast<int>(_dims.size())) {
*this = Chunking(plist, 8 * max_dims);
} else {
_dims.resize(static_cast<size_t>(n_loaded));
}
}
inline const std::vector<hsize_t>& Chunking::getDimensions() const {
return _dims;
}
template <typename... Args>
inline Chunking::Chunking(hsize_t item, Args... args)
: Chunking(std::vector<hsize_t>{item, static_cast<hsize_t>(args)...}) {}
inline void Deflate::apply(const hid_t hid) const {
if (detail::h5z_filter_avail(H5Z_FILTER_DEFLATE) == 0) {
HDF5ErrMapper::ToException<PropertyException>("Deflate filter unavailable.");
}
detail::h5p_set_deflate(hid, _level);
}
inline Deflate::Deflate(unsigned int level)
: _level(level) {}
inline void Szip::apply(const hid_t hid) const {
if (detail::h5z_filter_avail(H5Z_FILTER_SZIP) == 0) {
HDF5ErrMapper::ToException<PropertyException>("SZIP filter unavailable.");
}
detail::h5p_set_szip(hid, _options_mask, _pixels_per_block);
}
inline Szip::Szip(unsigned int options_mask, unsigned int pixels_per_block)
: _options_mask(options_mask)
, _pixels_per_block(pixels_per_block) {}
inline unsigned Szip::getOptionsMask() const {
return _options_mask;
}
inline unsigned Szip::getPixelsPerBlock() const {
return _pixels_per_block;
}
inline void Shuffle::apply(const hid_t hid) const {
if (detail::h5z_filter_avail(H5Z_FILTER_SHUFFLE) == 0) {
HDF5ErrMapper::ToException<PropertyException>("Shuffle filter unavailable.");
}
detail::h5p_set_shuffle(hid);
}
inline AllocationTime::AllocationTime(H5D_alloc_time_t alloc_time)
: _alloc_time(alloc_time) {}
inline AllocationTime::AllocationTime(const DataSetCreateProps& dcpl) {
detail::h5p_get_alloc_time(dcpl.getId(), &_alloc_time);
}
inline void AllocationTime::apply(hid_t dcpl) const {
detail::h5p_set_alloc_time(dcpl, _alloc_time);
}
inline H5D_alloc_time_t AllocationTime::getAllocationTime() {
return _alloc_time;
}
inline Caching::Caching(const DataSetCreateProps& dcpl) {
detail::h5p_get_chunk_cache(dcpl.getId(), &_numSlots, &_cacheSize, &_w0);
}
inline void Caching::apply(const hid_t hid) const {
detail::h5p_set_chunk_cache(hid, _numSlots, _cacheSize, _w0);
}
inline Caching::Caching(const size_t numSlots, const size_t cacheSize, const double w0)
: _numSlots(numSlots)
, _cacheSize(cacheSize)
, _w0(w0) {}
inline size_t Caching::getNumSlots() const {
return _numSlots;
}
inline size_t Caching::getCacheSize() const {
return _cacheSize;
}
inline double Caching::getW0() const {
return _w0;
}
inline CreateIntermediateGroup::CreateIntermediateGroup(bool create)
: _create(create) {}
inline void CreateIntermediateGroup::apply(const hid_t hid) const {
detail::h5p_set_create_intermediate_group(hid, _create ? 1 : 0);
}
inline CreateIntermediateGroup::CreateIntermediateGroup(const LinkCreateProps& lcpl) {
fromPropertyList(lcpl.getId());
}
inline void CreateIntermediateGroup::fromPropertyList(hid_t hid) {
unsigned c_bool = 0;
_create = bool(detail::h5p_get_create_intermediate_group(hid, &c_bool));
}
inline bool CreateIntermediateGroup::isSet() const {
return _create;
}
#ifdef H5_HAVE_PARALLEL
inline UseCollectiveIO::UseCollectiveIO(bool enable)
: _enable(enable) {}
inline void UseCollectiveIO::apply(const hid_t hid) const {
detail::h5p_set_dxpl_mpio(hid, _enable ? H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT);
}
inline UseCollectiveIO::UseCollectiveIO(const DataTransferProps& dxpl) {
H5FD_mpio_xfer_t collective;
detail::h5p_get_dxpl_mpio(dxpl.getId(), &collective);
if (collective != H5FD_MPIO_COLLECTIVE && collective != H5FD_MPIO_INDEPENDENT) {
throw std::logic_error("H5Pget_dxpl_mpio returned something strange.");
}
_enable = collective == H5FD_MPIO_COLLECTIVE;
}
inline bool UseCollectiveIO::isCollective() const {
return _enable;
}
inline MpioNoCollectiveCause::MpioNoCollectiveCause(const DataTransferProps& dxpl) {
detail::h5p_get_mpio_no_collective_cause(dxpl.getId(), &_local_cause, &_global_cause);
}
inline bool MpioNoCollectiveCause::wasCollective() const {
return _local_cause == 0 && _global_cause == 0;
}
inline uint32_t MpioNoCollectiveCause::getLocalCause() const {
return _local_cause;
}
inline uint32_t MpioNoCollectiveCause::getGlobalCause() const {
return _global_cause;
}
inline std::pair<uint32_t, uint32_t> MpioNoCollectiveCause::getCause() const {
return {_local_cause, _global_cause};
}
#endif
inline LinkCreationOrder::LinkCreationOrder(const FileCreateProps& fcpl) {
fromPropertyList(fcpl.getId());
}
inline LinkCreationOrder::LinkCreationOrder(const GroupCreateProps& gcpl) {
fromPropertyList(gcpl.getId());
}
inline unsigned LinkCreationOrder::getFlags() const {
return _flags;
}
inline void LinkCreationOrder::apply(const hid_t hid) const {
detail::h5p_set_link_creation_order(hid, _flags);
}
inline void LinkCreationOrder::fromPropertyList(hid_t hid) {
detail::h5p_get_link_creation_order(hid, &_flags);
}
inline AttributePhaseChange::AttributePhaseChange(unsigned max_compact, unsigned min_dense)
: _max_compact(max_compact)
, _min_dense(min_dense) {}
inline AttributePhaseChange::AttributePhaseChange(const GroupCreateProps& gcpl) {
detail::h5p_get_attr_phase_change(gcpl.getId(), &_max_compact, &_min_dense);
}
inline unsigned AttributePhaseChange::max_compact() const {
return _max_compact;
}
inline unsigned AttributePhaseChange::min_dense() const {
return _min_dense;
}
inline void AttributePhaseChange::apply(hid_t hid) const {
detail::h5p_set_attr_phase_change(hid, _max_compact, _min_dense);
}
} // namespace HighFive

View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2020 Blue Brain Project
*
* 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)
*
*/
#pragma once
#include <H5Tpublic.h>
#include "H5Inspector_misc.hpp"
#include "H5Utils.hpp"
namespace HighFive {
namespace details {
template <typename T>
using unqualified_t = typename std::remove_const<typename std::remove_reference<T>::type>::type;
// Find the type of an eventual char array, otherwise void
template <typename T>
struct type_char_array {
using type = typename std::conditional<
std::is_same<typename inspector<T>::base_type, std::string>::value,
std::string,
void>::type;
static constexpr bool is_char_array = false;
};
template <typename T>
struct type_char_array<T*> {
using type = typename std::conditional<std::is_same<unqualified_t<T>, char>::value,
char*,
typename type_char_array<T>::type>::type;
static constexpr bool is_char_array = true;
};
template <typename T, std::size_t N>
struct type_char_array<T[N]> {
using type = typename std::conditional<std::is_same<unqualified_t<T>, char>::value,
char[N],
typename type_char_array<T>::type>::type;
static constexpr bool is_char_array = true;
};
template <typename T>
struct BufferInfo {
using type_no_const = typename std::remove_const<T>::type;
using elem_type = typename details::inspector<type_no_const>::base_type;
using char_array_t = typename details::type_char_array<type_no_const>::type;
static constexpr bool is_char_array = details::type_char_array<type_no_const>::is_char_array;
enum class Operation { read, write };
const Operation op;
template <class F>
BufferInfo(const DataType& file_data_type, F getName, Operation _op);
size_t getRank(const T& array) const;
size_t getMinRank() const;
size_t getMaxRank() const;
// member data for info depending on the destination dataset type
const bool is_fixed_len_string;
const DataType data_type;
const size_t rank_correction;
};
// details implementation
template <typename SrcStrT>
struct string_type_checker {
static DataType getDataType(const DataType&, const DataType&);
};
inline void enforce_ascii_hack(const DataType& dst, const DataType& src) {
// Note: constness only refers to constness of the DataType object, which
// is just an ID, we can/will change properties of `dst`.
// TEMP. CHANGE: Ensure that the character set is properly configured to prevent
// converter issues on HDF5 <=v1.12.0 when loading ASCII strings first.
// See https://github.com/HDFGroup/hdf5/issues/544 for further information.
bool is_dst_string = detail::h5t_get_class(dst.getId()) == H5T_STRING;
bool is_src_string = detail::h5t_get_class(src.getId()) == H5T_STRING;
if (is_dst_string && is_src_string) {
if (detail::h5t_get_cset(src.getId()) == H5T_CSET_ASCII) {
detail::h5t_set_cset(dst.getId(), H5T_CSET_ASCII);
}
}
}
template <>
struct string_type_checker<void> {
inline static DataType getDataType(const DataType& element_type, const DataType& dtype) {
if (detail::h5t_get_class(element_type.getId()) == H5T_STRING) {
enforce_ascii_hack(element_type, dtype);
}
return element_type;
}
};
template <>
struct string_type_checker<std::string> {
inline static DataType getDataType(const DataType&, const DataType& file_datatype) {
// The StringBuffer ensures that the data is transformed such that it
// matches the datatype of the dataset, i.e. `file_datatype` and
// `mem_datatype` are the same.
return file_datatype;
}
};
template <std::size_t FixedLen>
struct string_type_checker<char[FixedLen]> {
inline static DataType getDataType(const DataType& element_type, const DataType& dtype) {
DataType return_type = (dtype.isFixedLenStr()) ? AtomicType<char[FixedLen]>()
: element_type;
enforce_ascii_hack(return_type, dtype);
return return_type;
}
};
template <>
struct string_type_checker<char*> {
inline static DataType getDataType(const DataType&, const DataType& dtype) {
if (dtype.isFixedLenStr()) {
throw DataSetException("Can't output variable-length to fixed-length strings");
}
DataType return_type = AtomicType<std::string>();
enforce_ascii_hack(return_type, dtype);
return return_type;
}
};
template <typename T>
template <class F>
BufferInfo<T>::BufferInfo(const DataType& file_data_type, F getName, Operation _op)
: op(_op)
, is_fixed_len_string(file_data_type.isFixedLenStr())
// In case we are using Fixed-len strings we need to subtract one dimension
, data_type(string_type_checker<char_array_t>::getDataType(create_datatype<elem_type>(),
file_data_type))
, rank_correction((is_fixed_len_string && is_char_array) ? 1 : 0) {
// We warn. In case they are really not convertible an exception will rise on read/write
if (file_data_type.getClass() != data_type.getClass()) {
HIGHFIVE_LOG_WARN(getName() + "\": data and hdf5 dataset have different types: " +
data_type.string() + " -> " + file_data_type.string());
} else if ((file_data_type.getClass() & data_type.getClass()) == DataTypeClass::Float) {
HIGHFIVE_LOG_WARN_IF(
(op == Operation::read) && (file_data_type.getSize() > data_type.getSize()),
getName() + "\": hdf5 dataset has higher floating point precision than data on read: " +
file_data_type.string() + " -> " + data_type.string());
HIGHFIVE_LOG_WARN_IF(
(op == Operation::write) && (file_data_type.getSize() < data_type.getSize()),
getName() +
"\": data has higher floating point precision than hdf5 dataset on write: " +
data_type.string() + " -> " + file_data_type.string());
}
}
template <typename T>
size_t BufferInfo<T>::getRank(const T& array) const {
return details::inspector<type_no_const>::getRank(array) - rank_correction;
}
template <typename T>
size_t BufferInfo<T>::getMinRank() const {
return details::inspector<T>::min_ndim - rank_correction;
}
template <typename T>
size_t BufferInfo<T>::getMaxRank() const {
return details::inspector<T>::max_ndim - rank_correction;
}
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,58 @@
/*
* Copyright (c), 2020, EPFL - Blue Brain Project
*
* 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)
*
*/
#pragma once
#include <string>
#include <H5Ppublic.h>
#include "H5Utils.hpp"
#include "../H5Object.hpp"
#include "h5r_wrapper.hpp"
namespace HighFive {
inline Reference::Reference(const Object& location, const Object& object)
: parent_id(location.getId()) {
obj_name = details::get_name([&](char* buffer, size_t length) {
return detail::h5i_get_name(object.getId(), buffer, length);
});
}
inline void Reference::create_ref(hobj_ref_t* refptr) const {
detail::h5r_create(refptr, parent_id, obj_name.c_str(), H5R_OBJECT, -1);
}
inline ObjectType Reference::getType(const Object& location) const {
return get_ref(location).getType();
}
template <typename T>
inline T Reference::dereference(const Object& location) const {
static_assert(std::is_same<DataSet, T>::value || std::is_same<Group, T>::value,
"We can only (de)reference HighFive::Group or HighFive:DataSet");
auto obj = get_ref(location);
if (obj.getType() != T::type) {
HDF5ErrMapper::ToException<ReferenceException>("Trying to dereference the wrong type");
}
return T(std::move(obj));
}
inline Object Reference::get_ref(const Object& location) const {
#if (H5Rdereference_vers == 2)
hid_t res = detail::h5r_dereference(location.getId(), H5P_DEFAULT, H5R_OBJECT, &href);
#else
hid_t res = detail::h5r_dereference(location.getId(), H5R_OBJECT, &href);
#endif
return Object(res);
}
} // namespace HighFive

View File

@ -0,0 +1,49 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
namespace HighFive {
inline Selection::Selection(const DataSpace& memspace,
const DataSpace& file_space,
const DataSet& set)
: _mem_space(memspace)
, _file_space(file_space)
, _set(set) {}
inline DataSpace Selection::getSpace() const {
return _file_space;
}
inline DataSpace Selection::getMemSpace() const {
return _mem_space;
}
inline DataSet& Selection::getDataset() {
return _set;
}
inline const DataSet& Selection::getDataset() const {
return _set;
}
// Not only a shortcut but also for templated compat with H5Dataset
inline DataType Selection::getDataType() const {
return _set.getDataType();
}
namespace detail {
inline Selection make_selection(const DataSpace& mem_space,
const DataSpace& file_space,
const DataSet& set) {
return Selection(mem_space, file_space, set);
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,578 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <cstdlib>
#include <vector>
#include "H5_definitions.hpp"
#include "H5Utils.hpp"
#include "convert_size_vector.hpp"
#include "../H5PropertyList.hpp"
#include "h5s_wrapper.hpp"
namespace HighFive {
class ElementSet {
public:
///
/// \brief Create a list of points of N-dimension for selection.
///
/// \param list List of continuous coordinates (e.g.: in 2 dimensions space
/// `ElementSet{1, 2, 3 ,4}` creates points `(1, 2)` and `(3, 4)`).
ElementSet(std::initializer_list<std::size_t> list);
///
/// \brief Create a list of points of N-dimension for selection.
///
/// \param list List of N-dim points.
ElementSet(std::initializer_list<std::vector<std::size_t>> list);
///
/// \brief Create a list of points of N-dimension for selection.
///
/// \param element_ids List of continuous coordinates (e.g.: in 2 dimensions space
/// `ElementSet{1, 2, 3 ,4}` creates points `(1, 2)` and `(3, 4)`).
explicit ElementSet(const std::vector<std::size_t>& element_ids);
///
/// \brief Create a list of points of N-dimension for selection.
///
/// \param element_ids List of N-dim points.
explicit ElementSet(const std::vector<std::vector<std::size_t>>& element_ids);
private:
std::vector<std::size_t> _ids;
template <typename Derivate>
friend class SliceTraits;
};
inline std::vector<hsize_t> toHDF5SizeVector(const std::vector<size_t>& from) {
return detail::convertSizeVector<hsize_t>(from);
}
inline std::vector<size_t> toSTLSizeVector(const std::vector<hsize_t>& from) {
return detail::convertSizeVector<size_t>(from);
}
struct RegularHyperSlab {
RegularHyperSlab() = default;
explicit RegularHyperSlab(const std::vector<size_t>& offset_,
const std::vector<size_t>& count_ = {},
const std::vector<size_t>& stride_ = {},
const std::vector<size_t>& block_ = {})
: offset(toHDF5SizeVector(offset_))
, count(toHDF5SizeVector(count_))
, stride(toHDF5SizeVector(stride_))
, block(toHDF5SizeVector(block_)) {}
static RegularHyperSlab fromHDF5Sizes(std::vector<hsize_t> offset_,
std::vector<hsize_t> count_ = {},
std::vector<hsize_t> stride_ = {},
std::vector<hsize_t> block_ = {}) {
RegularHyperSlab slab;
slab.offset = std::move(offset_);
slab.count = std::move(count_);
slab.stride = std::move(stride_);
slab.block = std::move(block_);
return slab;
}
size_t rank() const {
return std::max(std::max(offset.size(), count.size()),
std::max(stride.size(), block.size()));
}
/// Dimensions when all gaps are removed.
std::vector<size_t> packedDims() const {
auto n_dims = rank();
auto dims = std::vector<size_t>(n_dims, 0);
for (size_t i = 0; i < n_dims; ++i) {
dims[i] = count[i] * (block.empty() ? 1 : block[i]);
}
return dims;
}
std::vector<hsize_t> offset;
std::vector<hsize_t> count;
std::vector<hsize_t> stride;
std::vector<hsize_t> block;
};
class HyperSlab {
public:
HyperSlab() {
selects.emplace_back(RegularHyperSlab{}, Op::None);
};
explicit HyperSlab(const RegularHyperSlab& sel) {
selects.emplace_back(sel, Op::Set);
}
HyperSlab operator|(const RegularHyperSlab& sel) const {
auto ret = *this;
ret |= sel;
return ret;
}
HyperSlab& operator|=(const RegularHyperSlab& sel) {
selects.emplace_back(sel, Op::Or);
return *this;
}
HyperSlab operator&(const RegularHyperSlab& sel) const {
auto ret = *this;
ret &= sel;
return ret;
}
HyperSlab& operator&=(const RegularHyperSlab& sel) {
selects.emplace_back(sel, Op::And);
return *this;
}
HyperSlab operator^(const RegularHyperSlab& sel) const {
auto ret = *this;
ret ^= sel;
return ret;
}
HyperSlab& operator^=(const RegularHyperSlab& sel) {
selects.emplace_back(sel, Op::Xor);
return *this;
}
HyperSlab& notA(const RegularHyperSlab& sel) {
selects.emplace_back(sel, Op::NotA);
return *this;
}
HyperSlab& notB(const RegularHyperSlab& sel) {
selects.emplace_back(sel, Op::NotB);
return *this;
}
DataSpace apply(const DataSpace& space_) const {
return apply_impl(space_);
}
private:
enum class Op {
Noop,
Set,
Or,
And,
Xor,
NotB,
NotA,
Append,
Prepend,
Invalid,
None,
};
H5S_seloper_t convert(Op op) const {
switch (op) {
case Op::Noop:
return H5S_SELECT_NOOP;
case Op::Set:
return H5S_SELECT_SET;
case Op::Or:
return H5S_SELECT_OR;
case Op::And:
return H5S_SELECT_AND;
case Op::Xor:
return H5S_SELECT_XOR;
case Op::NotB:
return H5S_SELECT_NOTB;
case Op::NotA:
return H5S_SELECT_NOTA;
case Op::Append:
return H5S_SELECT_APPEND;
case Op::Prepend:
return H5S_SELECT_PREPEND;
case Op::Invalid:
return H5S_SELECT_INVALID;
default:
throw DataSpaceException("Invalid HyperSlab operation.");
}
}
struct Select_: public RegularHyperSlab {
Select_(const RegularHyperSlab& sel, Op op_)
: RegularHyperSlab(sel)
, op(op_) {}
Op op;
};
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 const* 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 const* const begin = selects.data() + i;
auto const* const 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
};
///
/// \brief Selects the Cartesian product of slices.
///
/// Given a one-dimensional dataset one might want to select the union of
/// multiple, non-overlapping slices. For example,
///
/// using Slice = std::array<size_t, 2>;
/// using Slices = std::vector<Slice>;
/// auto slices = Slices{{0, 2}, {4, 10}};
/// dset.select(ProductSet(slices);
///
/// to select elements `0`, `1` and `4`, ..., `9` (inclusive).
///
/// For a two-dimensional array one which to select the row specified above,
/// but only columns `2`, `3` and `4`:
///
/// dset.select(ProductSet(slices, Slice{2, 5}));
/// // Analogues with the roles of columns and rows reversed:
/// dset.select(ProductSet(Slice{2, 5}, slices));
///
/// One can generalize once more and allow the unions of slices in both x- and
/// y-dimension:
///
/// auto yslices = Slices{{1, 5}, {7, 8}};
/// auto xslices = Slices{{0, 3}, {6, 8}};
/// dset.select(ProductSet(yslices, xslices));
///
/// which selects the following from a 11x8 dataset:
///
/// . . . . . . . .
/// x x x . . . x x
/// x x x . . . x x
/// x x x . . . x x
/// x x x . . . x x
/// . . . . . . . .
/// . . . . . . . .
/// x x x . . . x x
/// . . . . . . . .
/// . . . . . . . .
/// . . . . . . . .
///
/// Final twist, the selection along and axis may be discrete indices, from
/// which a vector of, possibly single-element, slices can be constructed. The
/// corresponding types are `std::vector<size_t>` and `size_t` for multiple or
/// just a single values. Note, looping over rows or columns one-by-one can be
/// a very serious performance problem. In particular,
///
/// // Avoid:
/// for(auto i : indices) {
/// dset.select(i).read<double>();
/// }
///
/// // Use:
/// std::vector<size_t> tmp(indices.begin(), indices.end());
/// dset.select(tmp).read<std::vector<double>>();
///
/// obvious omit the copy if `indices` already has the correct type.
///
/// The solution works analogous in higher dimensions. A selection `sk` along
/// axis `k` can be interpreted as a subset `S_k` of the natural numbers. The
/// index `i` is in `S_k` if it's selected by `sk`. The `ProductSet` of `s0`,
/// ..., `sN` selects the Cartesian product `S_0 x ... x S_N`.
///
/// Note that the selections along each axis must be sorted and non-overlapping.
///
/// \since 3.0
class ProductSet {
public:
template <class... Slices>
explicit ProductSet(const Slices&... slices);
private:
HyperSlab slab;
std::vector<size_t> shape;
template <typename Derivate>
friend class SliceTraits;
};
template <typename Derivate>
class SliceTraits {
public:
///
/// \brief Select an \p hyper_slab in the current Slice/Dataset.
///
/// HyperSlabs can be either regular or irregular. Irregular hyperslabs are typically generated
/// by taking the union of regular hyperslabs. An irregular hyperslab, in general, does not fit
/// 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;
///
/// \brief Select an \p hyper_slab in the current Slice/Dataset.
///
/// If the selection can be read into a simple, multi-dimensional dataspace,
/// then this overload enable specifying the shape of the memory dataspace
/// with `memspace`. Note, that simple implies no offsets, strides or
/// number of blocks, just the size of the block in each dimension.
Selection select(const HyperSlab& hyper_slab, const DataSpace& memspace) const;
///
/// \brief Select a region in the current Slice/Dataset of \p count points at
/// \p offset separated by \p stride. If strides are not provided they will
/// default to 1 in all dimensions.
///
/// vector offset and count have to be from the same dimension
///
Selection select(const std::vector<size_t>& offset,
const std::vector<size_t>& count,
const std::vector<size_t>& stride = {},
const std::vector<size_t>& block = {}) const;
///
/// \brief Select a set of columns in the last dimension of this dataset.
///
/// The column indices must be smaller than the dimension size.
///
Selection select(const std::vector<size_t>& columns) const;
///
/// \brief Select a region in the current Slice/Dataset out of a list of elements.
///
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>
T read(const DataTransferProps& xfer_props = DataTransferProps()) const;
///
/// Read the entire dataset into a buffer
///
/// An exception is raised is if the numbers of dimension of the buffer and
/// of the dataset are different.
///
/// The array type can be a N-pointer or a N-vector. For plain pointers
/// not dimensionality checking will be performed, it is the user's
/// responsibility to ensure that the right amount of space has been
/// allocated.
template <typename T>
void read(T& array, const DataTransferProps& xfer_props = DataTransferProps()) const;
///
/// Read the entire dataset into a raw buffer
///
/// No dimensionality checks will be performed, it is the user's
/// responsibility to ensure that the right amount of space has been
/// allocated.
/// \param array: A buffer containing enough space for the data
/// \param mem_datatype: The type of the data in memory. This prevents deducing it from T.
/// \param xfer_props: Data Transfer properties
template <typename T>
void read_raw(T* array,
const DataType& mem_datatype,
const DataTransferProps& xfer_props = DataTransferProps()) const;
///
/// Read the entire dataset into a raw buffer
///
/// Same as `read(T*, const DataType&, const DataTransferProps&)`. However,
/// this overload deduces the HDF5 datatype of the element of `array` from
/// `T`. Note, that the file datatype is already fixed.
///
/// \param array: A buffer containing enough space for the data
/// \param xfer_props: Data Transfer properties
template <typename T>
void read_raw(T* array, const DataTransferProps& xfer_props = DataTransferProps()) const;
///
/// Write the integrality N-dimension buffer to this dataset
/// An exception is raised is if the numbers of dimension of the buffer and
/// of the dataset are different
///
/// The array type can be a N-pointer or a N-vector ( e.g int** integer two
/// dimensional array )
template <typename T>
void write(const T& buffer, const DataTransferProps& xfer_props = DataTransferProps());
///
/// Write from a raw pointer into this dataset.
///
/// No dimensionality checks will be performed, it is the user's
/// responsibility to ensure that the buffer holds the right amount of
/// elements. For n-dimensional matrices the buffer layout follows H5
/// default conventions.
///
/// Note, this is the shallowest wrapper around `H5Dwrite` and should
/// be used if full control is needed. Generally prefer `write`.
///
/// \param buffer: A buffer containing the data to be written
/// \param dtype: The datatype of `buffer`, i.e. the memory data type.
/// \param xfer_props: The HDF5 data transfer properties, e.g. collective MPI-IO.
template <typename T>
void write_raw(const T* buffer,
const DataType& mem_datatype,
const DataTransferProps& xfer_props = DataTransferProps());
///
/// Write from a raw pointer into this dataset.
///
/// Same as `write_raw(const T*, const DataTransferProps&)`. However, this
/// overload attempts to guess the data type of `buffer`, i.e. the memory
/// datatype. Note that the file datatype is already fixed.
///
template <typename T>
void write_raw(const T* buffer, const DataTransferProps& xfer_props = DataTransferProps());
///
/// \brief Return a `Selection` with `axes` squeezed from the memspace.
///
/// Returns a selection in which the memspace has been modified
/// to not include the axes listed in `axes`.
///
/// Throws if any axis to be squeezes has a dimension other than `1`.
///
/// \since 3.0
Selection squeezeMemSpace(const std::vector<size_t>& axes) const;
///
/// \brief Return a `Selection` with a simple memspace with `dims`.
///
/// Returns a selection in which the memspace has been modified
/// to be a simple dataspace with dimensions `dims`.
///
/// Throws if the number of elements changes.
///
/// \since 3.0
Selection reshapeMemSpace(const std::vector<size_t>& dims) const;
};
} // namespace HighFive

View File

@ -0,0 +1,483 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include <algorithm>
#include <cassert>
#include <functional>
#include <numeric>
#include <sstream>
#include <string>
#include "h5d_wrapper.hpp"
#include "h5s_wrapper.hpp"
#include "H5ReadWrite_misc.hpp"
#include "H5Converter_misc.hpp"
#include "squeeze.hpp"
#include "compute_total_size.hpp"
#include "assert_compatible_spaces.hpp"
namespace HighFive {
namespace details {
// map the correct reference to the dataset depending of the layout
// dataset -> itself
// subselection -> parent dataset
inline const DataSet& get_dataset(const Selection& sel) {
return sel.getDataset();
}
inline const DataSet& get_dataset(const DataSet& ds) {
return ds;
}
// map the correct memspace identifier depending of the layout
// dataset -> entire memspace
// selection -> resolve space id
inline hid_t get_memspace_id(const Selection& ptr) {
return ptr.getMemSpace().getId();
}
inline hid_t get_memspace_id(const DataSet&) {
return H5S_ALL;
}
} // namespace details
inline ElementSet::ElementSet(std::initializer_list<std::size_t> list)
: _ids(list) {}
inline ElementSet::ElementSet(std::initializer_list<std::vector<std::size_t>> list)
: ElementSet(std::vector<std::vector<std::size_t>>(list)) {}
inline ElementSet::ElementSet(const std::vector<std::size_t>& element_ids)
: _ids(element_ids) {}
inline ElementSet::ElementSet(const std::vector<std::vector<std::size_t>>& element_ids) {
for (const auto& vec: element_ids) {
std::copy(vec.begin(), vec.end(), std::back_inserter(_ids));
}
}
namespace detail {
class HyperCube {
public:
explicit HyperCube(size_t rank)
: offset(rank)
, count(rank) {}
void cross(const std::array<size_t, 2>& range, size_t axis) {
offset[axis] = range[0];
count[axis] = range[1] - range[0];
}
RegularHyperSlab asSlab() {
return RegularHyperSlab(offset, count);
}
private:
std::vector<size_t> offset;
std::vector<size_t> count;
};
inline void build_hyper_slab(HyperSlab& slab, size_t /* axis */, HyperCube& cube) {
slab |= cube.asSlab();
}
template <class... Slices>
inline void build_hyper_slab(HyperSlab& slab,
size_t axis,
HyperCube& cube,
const std::array<size_t, 2>& slice,
const Slices&... higher_slices) {
cube.cross(slice, axis);
build_hyper_slab(slab, axis + 1, cube, higher_slices...);
}
template <class... Slices>
inline void build_hyper_slab(HyperSlab& slab,
size_t axis,
HyperCube& cube,
const std::vector<std::array<size_t, 2>>& slices,
const Slices&... higher_slices) {
for (const auto& slice: slices) {
build_hyper_slab(slab, axis, cube, slice, higher_slices...);
}
}
template <class... Slices>
inline void build_hyper_slab(HyperSlab& slab,
size_t axis,
HyperCube& cube,
const std::vector<size_t>& ids,
const Slices&... higher_slices) {
for (const auto& id: ids) {
auto slice = std::array<size_t, 2>{id, id + 1};
build_hyper_slab(slab, axis, cube, slice, higher_slices...);
}
}
template <class... Slices>
inline void build_hyper_slab(HyperSlab& slab,
size_t axis,
HyperCube& cube,
size_t id,
const Slices&... higher_slices) {
auto slice = std::array<size_t, 2>{id, id + 1};
build_hyper_slab(slab, axis, cube, slice, higher_slices...);
}
inline void compute_squashed_shape(size_t /* axis */, std::vector<size_t>& /* shape */) {
// assert(axis == shape.size());
}
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
const std::array<size_t, 2>& slice,
const Slices&... higher_slices);
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
const std::vector<size_t>& points,
const Slices&... higher_slices);
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
size_t point,
const Slices&... higher_slices);
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
const std::vector<std::array<size_t, 2>>& slices,
const Slices&... higher_slices);
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
const std::array<size_t, 2>& slice,
const Slices&... higher_slices) {
shape[axis] = slice[1] - slice[0];
compute_squashed_shape(axis + 1, shape, higher_slices...);
}
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
const std::vector<size_t>& points,
const Slices&... higher_slices) {
shape[axis] = points.size();
compute_squashed_shape(axis + 1, shape, higher_slices...);
}
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
const std::vector<std::array<size_t, 2>>& slices,
const Slices&... higher_slices) {
shape[axis] = 0;
for (const auto& slice: slices) {
shape[axis] += slice[1] - slice[0];
}
compute_squashed_shape(axis + 1, shape, higher_slices...);
}
template <class... Slices>
inline void compute_squashed_shape(size_t axis,
std::vector<size_t>& shape,
size_t /* point */,
const Slices&... higher_slices) {
shape[axis] = 1;
compute_squashed_shape(axis + 1, shape, higher_slices...);
}
} // namespace detail
template <class... Slices>
inline ProductSet::ProductSet(const Slices&... slices) {
auto rank = sizeof...(slices);
detail::HyperCube cube(rank);
detail::build_hyper_slab(slab, 0, cube, slices...);
shape = std::vector<size_t>(rank, size_t(0));
detail::compute_squashed_shape(0, shape, slices...);
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab,
const DataSpace& memspace) const {
// Note: The current limitation are that memspace must describe a
// packed memspace.
//
// The reason for this is that we're unable to unpack general
// hyperslabs when the memory is not contiguous, e.g.
// `std::vector<std::vector<double>>`.
const auto& slice = static_cast<const Derivate&>(*this);
auto filespace = hyper_slab.apply(slice.getSpace());
return detail::make_selection(memspace, filespace, details::get_dataset(slice));
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const HyperSlab& 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)});
return detail::make_selection(memspace, filespace, details::get_dataset(slice));
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& offset,
const std::vector<size_t>& count,
const std::vector<size_t>& stride,
const std::vector<size_t>& block) const {
auto slab = HyperSlab(RegularHyperSlab(offset, count, stride, block));
auto memspace = DataSpace(count);
return select(slab, memspace);
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& columns) const {
const auto& slice = static_cast<const Derivate&>(*this);
const DataSpace& space = slice.getSpace();
std::vector<size_t> dims = space.getDimensions();
if (dims.empty()) {
throw DataSpaceException(
"Invalid, zero-dimensional (scalar) dataspace encountered when "
"selecting columns; must be atleast 1-dimensional.");
}
std::vector<size_t> counts = dims;
counts.back() = 1;
std::vector<size_t> offsets(dims.size(), 0);
HyperSlab slab;
for (const auto& column: columns) {
offsets.back() = column;
slab |= RegularHyperSlab(offsets, counts);
}
std::vector<size_t> memdims = dims;
memdims.back() = columns.size();
return select(slab, DataSpace(memdims));
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const ElementSet& elements) const {
const auto& slice = static_cast<const Derivate&>(*this);
const hsize_t* data = nullptr;
const DataSpace space = slice.getSpace().clone();
const std::size_t length = elements._ids.size();
if (length % space.getNumberDimensions() != 0) {
throw DataSpaceException(
"Number of coordinates in elements picking "
"should be a multiple of the dimensions.");
}
const std::size_t num_elements = length / space.getNumberDimensions();
std::vector<hsize_t> raw_elements;
// optimised at compile time
// switch for data conversion on 32bits platforms
if (std::is_same<std::size_t, hsize_t>::value) {
// `if constexpr` can't be used, thus a reinterpret_cast is needed.
data = reinterpret_cast<const hsize_t*>(elements._ids.data());
} else {
raw_elements.resize(length);
std::copy(elements._ids.begin(), elements._ids.end(), raw_elements.begin());
data = raw_elements.data();
}
detail::h5s_select_elements(space.getId(), H5S_SELECT_SET, num_elements, data);
return detail::make_selection(DataSpace(num_elements), space, details::get_dataset(slice));
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::select(const ProductSet& product_set) const {
return this->select(product_set.slab, DataSpace(product_set.shape));
}
template <typename Derivate>
template <typename T>
inline T SliceTraits<Derivate>::read(const DataTransferProps& xfer_props) const {
T array;
read(array, xfer_props);
return array;
}
template <typename Derivate>
template <typename T>
inline void SliceTraits<Derivate>::read(T& array, const DataTransferProps& xfer_props) const {
const auto& slice = static_cast<const Derivate&>(*this);
const DataSpace& mem_space = slice.getMemSpace();
auto file_datatype = slice.getDataType();
const details::BufferInfo<T> buffer_info(
file_datatype,
[&slice]() -> std::string { return details::get_dataset(slice).getPath(); },
details::BufferInfo<T>::Operation::read);
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to read DataSet of dimensions " << mem_space.getNumberDimensions()
<< " into arrays of dimensions: " << buffer_info.getMinRank() << "(min) to "
<< buffer_info.getMaxRank() << "(max)";
throw DataSpaceException(ss.str());
}
auto dims = mem_space.getDimensions();
auto r = details::data_converter::get_reader<T>(dims, array, file_datatype);
read_raw(r.getPointer(), buffer_info.data_type, xfer_props);
// re-arrange results
r.unserialize(array);
auto t = buffer_info.data_type;
auto c = t.getClass();
if (c == DataTypeClass::VarLen || t.isVariableStr()) {
#if H5_VERSION_GE(1, 12, 0)
// This one have been created in 1.12.0
(void)
detail::h5t_reclaim(t.getId(), mem_space.getId(), xfer_props.getId(), r.getPointer());
#else
// This one is deprecated since 1.12.0
(void) detail::h5d_vlen_reclaim(t.getId(),
mem_space.getId(),
xfer_props.getId(),
r.getPointer());
#endif
}
}
template <typename Derivate>
template <typename T>
inline void SliceTraits<Derivate>::read_raw(T* array,
const DataType& mem_datatype,
const DataTransferProps& xfer_props) const {
static_assert(!std::is_const<T>::value,
"read() requires a non-const structure to read data into");
const auto& slice = static_cast<const Derivate&>(*this);
detail::h5d_read(details::get_dataset(slice).getId(),
mem_datatype.getId(),
details::get_memspace_id(slice),
slice.getSpace().getId(),
xfer_props.getId(),
static_cast<void*>(array));
}
template <typename Derivate>
template <typename T>
inline void SliceTraits<Derivate>::read_raw(T* array, const DataTransferProps& xfer_props) const {
using element_type = typename details::inspector<T>::base_type;
const DataType& mem_datatype = create_and_check_datatype<element_type>();
read_raw(array, mem_datatype, xfer_props);
}
template <typename Derivate>
template <typename T>
inline void SliceTraits<Derivate>::write(const T& buffer, const DataTransferProps& xfer_props) {
const auto& slice = static_cast<const Derivate&>(*this);
const DataSpace& mem_space = slice.getMemSpace();
auto dims = mem_space.getDimensions();
auto file_datatype = slice.getDataType();
const details::BufferInfo<T> buffer_info(
file_datatype,
[&slice]() -> std::string { return details::get_dataset(slice).getPath(); },
details::BufferInfo<T>::Operation::write);
if (!details::checkDimensions(mem_space, buffer_info.getMinRank(), buffer_info.getMaxRank())) {
std::ostringstream ss;
ss << "Impossible to write buffer with dimensions n = " << buffer_info.getRank(buffer)
<< "into dataset with dimensions " << details::format_vector(mem_space.getDimensions())
<< ".";
throw DataSpaceException(ss.str());
}
auto w = details::data_converter::serialize<T>(buffer, dims, file_datatype);
write_raw(w.getPointer(), buffer_info.data_type, xfer_props);
}
template <typename Derivate>
template <typename T>
inline void SliceTraits<Derivate>::write_raw(const T* buffer,
const DataType& mem_datatype,
const DataTransferProps& xfer_props) {
const auto& slice = static_cast<const Derivate&>(*this);
detail::h5d_write(details::get_dataset(slice).getId(),
mem_datatype.getId(),
details::get_memspace_id(slice),
slice.getSpace().getId(),
xfer_props.getId(),
static_cast<const void*>(buffer));
}
template <typename Derivate>
template <typename T>
inline void SliceTraits<Derivate>::write_raw(const T* buffer, const DataTransferProps& xfer_props) {
using element_type = typename details::inspector<T>::base_type;
const auto& mem_datatype = create_and_check_datatype<element_type>();
write_raw(buffer, mem_datatype, xfer_props);
}
namespace detail {
inline const DataSet& getDataSet(const Selection& selection) {
return selection.getDataset();
}
inline const DataSet& getDataSet(const DataSet& dataset) {
return dataset;
}
} // namespace detail
template <typename Derivate>
inline Selection SliceTraits<Derivate>::squeezeMemSpace(const std::vector<size_t>& axes) const {
auto slice = static_cast<const Derivate&>(*this);
auto mem_dims = slice.getMemSpace().getDimensions();
auto squeezed_dims = detail::squeeze(mem_dims, axes);
return detail::make_selection(DataSpace(squeezed_dims),
slice.getSpace(),
detail::getDataSet(slice));
}
template <typename Derivate>
inline Selection SliceTraits<Derivate>::reshapeMemSpace(const std::vector<size_t>& new_dims) const {
auto slice = static_cast<const Derivate&>(*this);
detail::assert_compatible_spaces(slice.getMemSpace(), new_dims);
return detail::make_selection(DataSpace(new_dims), slice.getSpace(), detail::getDataSet(slice));
}
} // namespace HighFive

View File

@ -0,0 +1,77 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
// internal utilities functions
#include <algorithm>
#include <array>
#include <cstddef> // __GLIBCXX__
#include <exception>
#include <string>
#include <type_traits>
#include <vector>
#include <sstream>
#include <H5public.h>
#include "../H5Exception.hpp"
#include "H5Friends.hpp"
namespace HighFive {
namespace details {
// converter function for hsize_t -> size_t when hsize_t != size_t
template <typename Size>
inline std::vector<std::size_t> to_vector_size_t(const std::vector<Size>& vec) {
static_assert(!std::is_same<Size, std::size_t>::value, " hsize_t != size_t mandatory here");
std::vector<size_t> res(vec.size());
std::transform(vec.cbegin(), vec.cend(), res.begin(), [](Size e) {
return static_cast<size_t>(e);
});
return res;
}
// converter function for hsize_t -> size_t when size_t == hsize_t
inline std::vector<std::size_t> to_vector_size_t(const std::vector<std::size_t>& vec) {
return vec;
}
// read name from a H5 object using the specified function
template <typename T>
inline std::string get_name(T fct) {
constexpr size_t maxLength = 255;
std::array<char, maxLength + 1> buffer;
ssize_t retcode = fct(buffer.data(), static_cast<hsize_t>(buffer.size()));
if (retcode < 0) {
HDF5ErrMapper::ToException<GroupException>("Error accessing object name");
}
const size_t length = static_cast<std::size_t>(retcode);
if (length <= maxLength) {
return std::string(buffer.data(), length);
}
std::vector<char> bigBuffer(length + 1, 0);
fct(bigBuffer.data(), length + 1);
return std::string(bigBuffer.data(), length);
}
template <class Container>
inline std::string format_vector(const Container& container) {
auto sout = std::stringstream{};
sout << "[ ";
for (size_t i = 0; i < container.size(); ++i) {
sout << container[i] << (i == container.size() - 1 ? "" : ", ");
}
sout << "]";
return sout.str();
}
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,46 @@
#pragma once
#if defined(__GNUC__) || defined(__clang__)
#define H5_DEPRECATED(msg) __attribute__((deprecated(#msg)))
#elif defined(_MSC_VER)
#define H5_DEPRECATED(msg) __declspec(deprecated(#msg))
#else
#pragma message("WARNING: Compiler doesn't support deprecation")
#define H5_DEPRECATED(msg)
#endif
// Forward declarations
namespace HighFive {
enum class LinkType;
enum class ObjectType;
enum class PropertyType;
class Attribute;
class DataSet;
class DataSpace;
class DataType;
class Exception;
class File;
class FileDriver;
class Group;
class Object;
class ObjectInfo;
class Reference;
class Selection;
class SilenceHDF5;
template <typename T>
class AtomicType;
template <typename Derivate>
class AnnotateTraits;
template <typename Derivate>
class NodeTraits;
template <PropertyType T>
class PropertyList;
} // namespace HighFive

View File

@ -0,0 +1,29 @@
/*
* Copyright (c), 2024, BlueBrain Project, EPFL
*
* 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)
*
*/
#pragma once
#include <vector>
#include "../H5Exception.hpp"
#include "../H5DataSpace.hpp"
namespace HighFive {
namespace detail {
inline void assert_compatible_spaces(const DataSpace& old, const std::vector<size_t>& dims) {
auto n_elements_old = old.getElementCount();
auto n_elements_new = dims.empty() ? 1 : compute_total_size(dims);
if (n_elements_old != n_elements_new) {
throw Exception("Invalid parameter `new_dims` number of elements differ: " +
std::to_string(n_elements_old) + " (old) vs. " +
std::to_string(n_elements_new) + " (new)");
}
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,14 @@
#pragma once
#include <cstddef>
#include <numeric>
#include <functional>
#include <vector>
namespace HighFive {
inline size_t compute_total_size(const std::vector<size_t>& dims) {
return std::accumulate(dims.begin(), dims.end(), size_t{1u}, std::multiplies<size_t>());
}
} // namespace HighFive

View File

@ -0,0 +1,31 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
* Copyright (c), 2017-2024, BlueBrain Project, EPFL
*
* 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)
*
*/
#pragma once
#include <vector>
namespace HighFive {
namespace detail {
template <class To, class From, class It = From const*>
inline std::vector<To> convertSizeVector(const It& begin, const It& end) {
std::vector<To> to(static_cast<size_t>(end - begin));
std::copy(begin, end, to.begin());
return to;
}
template <class To, class From>
inline std::vector<To> convertSizeVector(const std::vector<From>& from) {
return convertSizeVector<To, From>(from.cbegin(), from.cend());
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,19 @@
#pragma once
#include <H5public.h>
namespace HighFive {
namespace detail {
inline void h5_free_memory(void* mem) {
if (H5free_memory(mem) < 0) {
throw DataTypeException("Could not free memory allocated by HDF5");
}
}
namespace nothrow {
inline herr_t h5_free_memory(void* mem) {
return H5free_memory(mem);
}
} // namespace nothrow
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,131 @@
#pragma once
#include <H5Apublic.h>
#include <H5Ipublic.h>
namespace HighFive {
namespace detail {
inline hid_t h5a_create2(hid_t loc_id,
char const* const attr_name,
hid_t type_id,
hid_t space_id,
hid_t acpl_id,
hid_t aapl_id) {
auto attr_id = H5Acreate2(loc_id, attr_name, type_id, space_id, acpl_id, aapl_id);
if (attr_id < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to create the attribute \"") + attr_name + "\":");
}
return attr_id;
}
inline void h5a_delete(hid_t loc_id, char const* const attr_name) {
if (H5Adelete(loc_id, attr_name) < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to delete attribute \"") + attr_name + "\":");
}
}
inline hid_t h5a_open(hid_t loc_id, char const* const attr_name, hid_t aapl_id) {
const auto attr_id = H5Aopen(loc_id, attr_name, aapl_id);
if (attr_id < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to open the attribute \"") + attr_name + "\":");
}
return attr_id;
}
inline int h5a_get_num_attrs(hid_t loc_id) {
int res = H5Aget_num_attrs(loc_id);
if (res < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to count attributes in existing group or file"));
}
return res;
}
inline void h5a_iterate2(hid_t loc_id,
H5_index_t idx_type,
H5_iter_order_t order,
hsize_t* idx,
H5A_operator2_t op,
void* op_data) {
if (H5Aiterate2(loc_id, idx_type, order, idx, op, op_data) < 0) {
HDF5ErrMapper::ToException<AttributeException>(std::string("Failed H5Aiterate2."));
}
}
inline int h5a_exists(hid_t obj_id, char const* const attr_name) {
int res = H5Aexists(obj_id, attr_name);
if (res < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to check for attribute in group"));
}
return res;
}
inline ssize_t h5a_get_name(hid_t attr_id, size_t buf_size, char* buf) {
ssize_t name_length = H5Aget_name(attr_id, buf_size, buf);
if (name_length < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to get name of attribute"));
}
return name_length;
}
inline hid_t h5a_get_space(hid_t attr_id) {
hid_t attr = H5Aget_space(attr_id);
if (attr < 0) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to get dataspace of attribute"));
}
return attr;
}
inline hsize_t h5a_get_storage_size(hid_t attr_id) {
// Docs:
// Returns the amount of storage size allocated for the attribute;
// otherwise returns 0 (zero).
return H5Aget_storage_size(attr_id);
}
inline hid_t h5a_get_type(hid_t attr_id) {
hid_t type_id = H5Aget_type(attr_id);
if (type_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<AttributeException>(
std::string("Unable to get datatype of attribute"));
}
return type_id;
}
inline herr_t h5a_read(hid_t attr_id, hid_t type_id, void* buf) {
herr_t err = H5Aread(attr_id, type_id, buf);
if (err < 0) {
HDF5ErrMapper::ToException<AttributeException>(std::string("Unable to read attribute"));
}
return err;
}
inline herr_t h5a_write(hid_t attr_id, hid_t type_id, void const* buf) {
herr_t err = H5Awrite(attr_id, type_id, buf);
if (err < 0) {
HDF5ErrMapper::ToException<AttributeException>(std::string("Unable to write attribute"));
}
return err;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,125 @@
#pragma once
#include <H5Dpublic.h>
#include <H5Ipublic.h>
namespace HighFive {
namespace detail {
#if !H5_VERSION_GE(1, 12, 0)
inline herr_t h5d_vlen_reclaim(hid_t type_id, hid_t space_id, hid_t dxpl_id, void* buf) {
herr_t err = H5Dvlen_reclaim(type_id, space_id, dxpl_id, buf);
if (err < 0) {
throw DataSetException("Failed to reclaim HDF5 internal memory");
}
return err;
}
#endif
inline hsize_t h5d_get_storage_size(hid_t dset_id) {
// Docs:
// H5Dget_storage_size() does not differentiate between 0 (zero), the
// value returned for the storage size of a dataset with no stored values,
// and 0 (zero), the value returned to indicate an error.
return H5Dget_storage_size(dset_id);
}
inline hid_t h5d_get_space(hid_t dset_id) {
hid_t dset = H5Dget_space(dset_id);
if (dset == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataSetException>(
std::string("Unable to get dataspace of the dataset"));
}
return dset;
}
inline hid_t h5d_get_type(hid_t dset_id) {
hid_t type_id = H5Dget_type(dset_id);
if (type_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataSetException>(
std::string("Unable to get datatype of the dataset"));
}
return type_id;
}
inline herr_t h5d_read(hid_t dset_id,
hid_t mem_type_id,
hid_t mem_space_id,
hid_t file_space_id,
hid_t dxpl_id,
void* buf) {
herr_t err = H5Dread(dset_id, mem_type_id, mem_space_id, file_space_id, dxpl_id, buf);
if (err < 0) {
HDF5ErrMapper::ToException<DataSetException>(std::string("Unable to read the dataset"));
}
return err;
}
inline herr_t h5d_write(hid_t dset_id,
hid_t mem_type_id,
hid_t mem_space_id,
hid_t file_space_id,
hid_t dxpl_id,
const void* buf) {
herr_t err = H5Dwrite(dset_id, mem_type_id, mem_space_id, file_space_id, dxpl_id, buf);
if (err < 0) {
HDF5ErrMapper::ToException<DataSetException>(std::string("Unable to write the dataset"));
}
return err;
}
inline haddr_t h5d_get_offset(hid_t dset_id) {
uint64_t addr = H5Dget_offset(dset_id);
if (addr == HADDR_UNDEF) {
HDF5ErrMapper::ToException<DataSetException>("Cannot get offset of DataSet.");
}
return addr;
}
inline herr_t h5d_set_extent(hid_t dset_id, const hsize_t size[]) {
herr_t err = H5Dset_extent(dset_id, size);
if (err < 0) {
HDF5ErrMapper::ToException<DataSetException>("Could not resize dataset.");
}
return err;
}
inline hid_t h5d_create2(hid_t loc_id,
const char* name,
hid_t type_id,
hid_t space_id,
hid_t lcpl_id,
hid_t dcpl_id,
hid_t dapl_id) {
hid_t dataset_id = H5Dcreate2(loc_id, name, type_id, space_id, lcpl_id, dcpl_id, dapl_id);
if (dataset_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataSetException>(
std::string("Failed to create the dataset \"") + name + "\":");
}
return dataset_id;
}
inline hid_t h5d_open2(hid_t loc_id, const char* name, hid_t dapl_id) {
hid_t dataset_id = H5Dopen2(loc_id, name, dapl_id);
if (dataset_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataSetException>(std::string("Unable to open the dataset \"") +
name + "\":");
}
return dataset_id;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,39 @@
#pragma once
#include <H5Epublic.h>
namespace HighFive {
namespace detail {
namespace nothrow {
inline void h5e_get_auto2(hid_t estack_id, H5E_auto2_t* func, void** client_data) {
H5Eget_auto2(estack_id, func, client_data);
}
inline void h5e_set_auto2(hid_t estack_id, H5E_auto2_t func, void* client_data) {
H5Eset_auto2(estack_id, func, client_data);
}
inline char* h5e_get_major(H5E_major_t maj) {
return H5Eget_major(maj);
}
inline char* h5e_get_minor(H5E_minor_t min) {
return H5Eget_minor(min);
}
inline herr_t h5e_walk2(hid_t err_stack,
H5E_direction_t direction,
H5E_walk2_t func,
void* client_data) {
return H5Ewalk2(err_stack, direction, func, client_data);
}
inline herr_t h5e_clear2(hid_t err_stack) {
return H5Eclear2(err_stack);
}
} // namespace nothrow
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,58 @@
#pragma once
#include <H5Fpublic.h>
namespace HighFive {
namespace detail {
namespace nothrow {
inline hid_t h5f_open(const char* filename, unsigned flags, hid_t fapl_id) {
return H5Fopen(filename, flags, fapl_id);
}
} // namespace nothrow
inline hid_t h5f_create(const char* filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id) {
hid_t file_id = H5Fcreate(filename, flags, fcpl_id, fapl_id);
if (file_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<FileException>(std::string("Failed to create file ") + filename);
}
return file_id;
}
inline ssize_t h5f_get_name(hid_t obj_id, char* name, size_t size) {
ssize_t nread = H5Fget_name(obj_id, name, size);
if (nread < 0) {
HDF5ErrMapper::ToException<FileException>(std::string("Failed to get file from id"));
}
return nread;
}
inline herr_t h5f_flush(hid_t object_id, H5F_scope_t scope) {
herr_t err = H5Fflush(object_id, scope);
if (err < 0) {
HDF5ErrMapper::ToException<FileException>(std::string("Failed to flush file"));
}
return err;
}
inline herr_t h5f_get_filesize(hid_t file_id, hsize_t* size) {
herr_t err = H5Fget_filesize(file_id, size);
if (err < 0) {
HDF5ErrMapper::ToException<FileException>(std::string("Unable to retrieve size of file"));
}
return err;
}
inline hssize_t h5f_get_freespace(hid_t file_id) {
hssize_t free_space = H5Fget_freespace(file_id);
if (free_space < 0) {
HDF5ErrMapper::ToException<FileException>(
std::string("Unable to retrieve unused space of file "));
}
return free_space;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,46 @@
#pragma once
#include <H5Dpublic.h>
#include <H5Ipublic.h>
#include <highfive/H5Exception.hpp>
namespace HighFive {
namespace detail {
inline hid_t h5g_create2(hid_t loc_id,
const char* name,
hid_t lcpl_id,
hid_t gcpl_id,
hid_t gapl_id) {
hid_t group_id = H5Gcreate2(loc_id, name, lcpl_id, gcpl_id, gapl_id);
if (group_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to create the group \"") +
name + "\":");
}
return group_id;
}
inline hid_t h5g_open2(hid_t loc_id, const char* name, hid_t gapl_id) {
hid_t group_id = H5Gopen2(loc_id, name, gapl_id);
if (group_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to open the group \"") +
name + "\":");
}
return group_id;
}
inline herr_t h5g_get_num_objs(hid_t loc_id, hsize_t* num_objs) {
herr_t err = H5Gget_num_objs(loc_id, num_objs);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(
std::string("Unable to count objects in existing group or file"));
}
return err;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,79 @@
#pragma once
#include <H5Ipublic.h>
namespace HighFive {
namespace detail {
inline int h5i_inc_ref(hid_t id) {
auto count = H5Iinc_ref(id);
if (count < 0) {
throw ObjectException("Failed to increase reference count of HID");
}
return count;
}
namespace nothrow {
inline int h5i_dec_ref(hid_t id) {
return H5Idec_ref(id);
}
} // namespace nothrow
inline int h5i_dec_ref(hid_t id) {
int count = H5Idec_ref(id);
if (count < 0) {
throw ObjectException("Failed to decrease reference count of HID");
}
return count;
}
namespace nothrow {
inline htri_t h5i_is_valid(hid_t id) {
return H5Iis_valid(id);
}
} // namespace nothrow
inline htri_t h5i_is_valid(hid_t id) {
htri_t tri = H5Iis_valid(id);
if (tri < 0) {
throw ObjectException("Failed to check if HID is valid");
}
return tri;
}
inline H5I_type_t h5i_get_type(hid_t id) {
H5I_type_t type = H5Iget_type(id);
if (type == H5I_BADID) {
HDF5ErrMapper::ToException<ObjectException>("Failed to get type of HID");
}
return type;
}
template <class Exception>
inline hid_t h5i_get_file_id(hid_t id) {
hid_t file_id = H5Iget_file_id(id);
if (file_id < 0) {
HDF5ErrMapper::ToException<Exception>("Failed not obtain file HID of object");
}
return file_id;
}
inline ssize_t h5i_get_name(hid_t id, char* name, size_t size) {
ssize_t n_chars = H5Iget_name(id, name, size);
if (n_chars < 0) {
HDF5ErrMapper::ToException<ObjectException>("Failed to get name of HID.");
}
return n_chars;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,132 @@
#pragma once
#include <H5Lpublic.h>
namespace HighFive {
namespace detail {
inline herr_t h5l_create_external(const char* file_name,
const char* obj_name,
hid_t link_loc_id,
const char* link_name,
hid_t lcpl_id,
hid_t lapl_id) {
herr_t err = H5Lcreate_external(file_name, obj_name, link_loc_id, link_name, lcpl_id, lapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to create external link: "));
}
return err;
}
inline herr_t h5l_create_soft(const char* link_target,
hid_t link_loc_id,
const char* link_name,
hid_t lcpl_id,
hid_t lapl_id) {
herr_t err = H5Lcreate_soft(link_target, link_loc_id, link_name, lcpl_id, lapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to create soft link: "));
}
return err;
}
inline herr_t h5l_create_hard(hid_t cur_loc,
const char* cur_name,
hid_t dst_loc,
const char* dst_name,
hid_t lcpl_id,
hid_t lapl_id) {
herr_t err = H5Lcreate_hard(cur_loc, cur_name, dst_loc, dst_name, lcpl_id, lapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to create hard link: "));
}
return err;
}
inline herr_t h5l_get_info(hid_t loc_id, const char* name, H5L_info_t* linfo, hid_t lapl_id) {
herr_t err = H5Lget_info(loc_id, name, linfo, lapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to obtain info for link "));
}
return err;
}
inline herr_t h5l_delete(hid_t loc_id, const char* name, hid_t lapl_id) {
herr_t err = H5Ldelete(loc_id, name, lapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Invalid name for unlink() "));
}
return err;
}
inline htri_t h5l_exists(hid_t loc_id, const char* name, hid_t lapl_id) {
htri_t tri = H5Lexists(loc_id, name, lapl_id);
if (tri < 0) {
HDF5ErrMapper::ToException<GroupException>("Invalid link for exist()");
}
return tri;
}
namespace nothrow {
inline htri_t h5l_exists(hid_t loc_id, const char* name, hid_t lapl_id) {
return H5Lexists(loc_id, name, lapl_id);
}
} // namespace nothrow
inline herr_t h5l_iterate(hid_t grp_id,
H5_index_t idx_type,
H5_iter_order_t order,
hsize_t* idx,
H5L_iterate_t op,
void* op_data) {
herr_t err = H5Literate(grp_id, idx_type, order, idx, op, op_data);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to list objects in group"));
}
return err;
}
inline herr_t h5l_move(hid_t src_loc,
const char* src_name,
hid_t dst_loc,
const char* dst_name,
hid_t lcpl_id,
hid_t lapl_id) {
herr_t err = H5Lmove(src_loc, src_name, dst_loc, dst_name, lcpl_id, lapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to move link to \"") +
dst_name + "\":");
}
return err;
}
inline ssize_t h5l_get_name_by_idx(hid_t loc_id,
const char* group_name,
H5_index_t idx_type,
H5_iter_order_t order,
hsize_t n,
char* name,
size_t size,
hid_t lapl_id) {
ssize_t n_chars =
H5Lget_name_by_idx(loc_id, group_name, idx_type, order, n, name, size, lapl_id);
if (n_chars < 0) {
HDF5ErrMapper::ToException<GroupException>(
std::string("Unable to obtain link name from index."));
}
return n_chars;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,66 @@
#pragma once
#include <H5Opublic.h>
namespace HighFive {
namespace detail {
inline hid_t h5o_open(hid_t loc_id, const char* name, hid_t lapl_id) {
hid_t hid = H5Oopen(loc_id, name, lapl_id);
if (hid < 0) {
HDF5ErrMapper::ToException<GroupException>(std::string("Unable to open \"") + name + "\":");
}
return hid;
}
inline herr_t h5o_close(hid_t id) {
herr_t err = H5Oclose(id);
if (err < 0) {
HDF5ErrMapper::ToException<ObjectException>("Unable to close object.");
}
return err;
}
#if H5O_info_t_vers >= 1
using h5o_info1_t = H5O_info1_t;
#else
using h5o_info1_t = H5O_info_t;
#endif
inline herr_t h5o_get_info1(hid_t loc_id, h5o_info1_t* info) {
#if H5Oget_info_vers >= 1
herr_t err = H5Oget_info1(loc_id, info);
#else
herr_t err = H5Oget_info(loc_id, info);
#endif
if (err < 0) {
HDF5ErrMapper::ToException<ObjectException>("Unable to obtain info for object");
}
return err;
}
#if H5_VERSION_GE(1, 10, 3)
inline herr_t h5o_get_info2(hid_t loc_id, h5o_info1_t* info, unsigned fields) {
herr_t err = H5Oget_info2(loc_id, info, fields);
if (err < 0) {
HDF5ErrMapper::ToException<ObjectException>("Unable to obtain info for object");
}
return err;
}
#endif
#if H5_VERSION_GE(1, 12, 0)
inline herr_t h5o_get_info3(hid_t loc_id, H5O_info2_t* info, unsigned fields) {
herr_t err = H5Oget_info3(loc_id, info, fields);
if (err < 0) {
HDF5ErrMapper::ToException<ObjectException>("Unable to obtain info for object");
}
return err;
}
#endif
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,376 @@
#pragma once
#include <H5Ipublic.h>
#include <H5Ppublic.h>
namespace HighFive {
namespace detail {
inline hid_t h5p_create(hid_t cls_id) {
hid_t plist_id = H5Pcreate(cls_id);
if (plist_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<PropertyException>("Failed to create property list");
}
return plist_id;
}
#if H5_VERSION_GE(1, 10, 1)
inline herr_t h5p_set_file_space_strategy(hid_t plist_id,
H5F_fspace_strategy_t strategy,
hbool_t persist,
hsize_t threshold) {
herr_t err = H5Pset_file_space_strategy(plist_id, strategy, persist, threshold);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Unable to get file space strategy");
}
return err;
}
inline herr_t h5p_get_file_space_strategy(hid_t plist_id,
H5F_fspace_strategy_t* strategy,
hbool_t* persist,
hsize_t* threshold) {
herr_t err = H5Pget_file_space_strategy(plist_id, strategy, persist, threshold);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error getting file space strategy.");
}
return err;
}
inline herr_t h5p_set_file_space_page_size(hid_t plist_id, hsize_t fsp_size) {
herr_t err = H5Pset_file_space_page_size(plist_id, fsp_size);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting file space page size.");
}
return err;
}
inline herr_t h5p_get_file_space_page_size(hid_t plist_id, hsize_t* fsp_size) {
herr_t err = H5Pget_file_space_page_size(plist_id, fsp_size);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Unable to get file space page size");
}
return err;
}
#ifndef H5_HAVE_PARALLEL
inline herr_t h5p_get_page_buffer_size(hid_t plist_id,
size_t* buf_size,
unsigned* min_meta_perc,
unsigned* min_raw_perc) {
herr_t err = H5Pget_page_buffer_size(plist_id, buf_size, min_meta_perc, min_raw_perc);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting page buffer size.");
}
return err;
}
inline herr_t h5p_set_page_buffer_size(hid_t plist_id,
size_t buf_size,
unsigned min_meta_per,
unsigned min_raw_per) {
herr_t err = H5Pset_page_buffer_size(plist_id, buf_size, min_meta_per, min_raw_per);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting page buffer size.");
}
return err;
}
#endif
#endif
#ifdef H5_HAVE_PARALLEL
inline herr_t h5p_set_fapl_mpio(hid_t fapl_id, MPI_Comm comm, MPI_Info info) {
herr_t err = H5Pset_fapl_mpio(fapl_id, comm, info);
if (err < 0) {
HDF5ErrMapper::ToException<FileException>("Unable to set-up MPIO Driver configuration");
}
return err;
}
#if H5_VERSION_GE(1, 10, 0)
inline herr_t h5p_set_all_coll_metadata_ops(hid_t plist_id, hbool_t is_collective) {
herr_t err = H5Pset_all_coll_metadata_ops(plist_id, is_collective);
if (err < 0) {
HDF5ErrMapper::ToException<FileException>("Unable to request collective metadata reads");
}
return err;
}
inline herr_t h5p_get_all_coll_metadata_ops(hid_t plist_id, hbool_t* is_collective) {
herr_t err = H5Pget_all_coll_metadata_ops(plist_id, is_collective);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error loading MPI metadata read.");
}
return err;
}
inline herr_t h5p_set_coll_metadata_write(hid_t plist_id, hbool_t is_collective) {
herr_t err = H5Pset_coll_metadata_write(plist_id, is_collective);
if (err < 0) {
HDF5ErrMapper::ToException<FileException>("Unable to request collective metadata writes");
}
return err;
}
inline herr_t h5p_get_coll_metadata_write(hid_t plist_id, hbool_t* is_collective) {
herr_t err = H5Pget_coll_metadata_write(plist_id, is_collective);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error loading MPI metadata write.");
}
return err;
}
#endif
#endif
inline herr_t h5p_get_libver_bounds(hid_t plist_id, H5F_libver_t* low, H5F_libver_t* high) {
herr_t err = H5Pget_libver_bounds(plist_id, low, high);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Unable to access file version bounds");
}
return err;
}
inline herr_t h5p_set_libver_bounds(hid_t plist_id, H5F_libver_t low, H5F_libver_t high) {
herr_t err = H5Pset_libver_bounds(plist_id, low, high);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting file version bounds");
}
return err;
}
inline herr_t h5p_get_meta_block_size(hid_t fapl_id, hsize_t* size) {
herr_t err = H5Pget_meta_block_size(fapl_id, size);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Unable to access file metadata block size");
}
return err;
}
inline herr_t h5p_set_meta_block_size(hid_t fapl_id, hsize_t size) {
herr_t err = H5Pset_meta_block_size(fapl_id, size);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting metadata block size");
}
return err;
}
inline herr_t h5p_set_est_link_info(hid_t plist_id,
unsigned est_num_entries,
unsigned est_name_len) {
herr_t err = H5Pset_est_link_info(plist_id, est_num_entries, est_name_len);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting estimated link info");
}
return err;
}
inline herr_t h5p_get_est_link_info(hid_t plist_id,
unsigned* est_num_entries,
unsigned* est_name_len) {
herr_t err = H5Pget_est_link_info(plist_id, est_num_entries, est_name_len);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Unable to access group link size property");
}
return err;
}
inline herr_t h5p_set_chunk(hid_t plist_id, int ndims, const hsize_t dim[]) {
herr_t err = H5Pset_chunk(plist_id, ndims, dim);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting chunk property");
}
return err;
}
inline int h5p_get_chunk(hid_t plist_id, int max_ndims, hsize_t dim[]) {
int chunk_dims = H5Pget_chunk(plist_id, max_ndims, dim);
if (chunk_dims < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error getting chunk size");
}
return chunk_dims;
}
inline htri_t h5z_filter_avail(H5Z_filter_t id) {
htri_t tri = H5Zfilter_avail(id);
if (tri < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error checking filter availability");
}
return tri;
}
inline herr_t h5p_set_deflate(hid_t plist_id, unsigned level) {
herr_t err = H5Pset_deflate(plist_id, level);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting deflate property");
}
return err;
}
inline herr_t h5p_set_szip(hid_t plist_id, unsigned options_mask, unsigned pixels_per_block) {
herr_t err = H5Pset_szip(plist_id, options_mask, pixels_per_block);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting szip property");
}
return err;
}
inline herr_t h5p_set_shuffle(hid_t plist_id) {
herr_t err = H5Pset_shuffle(plist_id);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting shuffle property");
}
return err;
}
inline herr_t h5p_get_alloc_time(hid_t plist_id, H5D_alloc_time_t* alloc_time) {
herr_t err = H5Pget_alloc_time(plist_id, alloc_time);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error getting allocation time");
}
return err;
}
inline herr_t h5p_set_alloc_time(hid_t plist_id, H5D_alloc_time_t alloc_time) {
herr_t err = H5Pset_alloc_time(plist_id, alloc_time);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting allocation time");
}
return err;
}
inline herr_t h5p_get_chunk_cache(hid_t dapl_id,
size_t* rdcc_nslots,
size_t* rdcc_nbytes,
double* rdcc_w0) {
herr_t err = H5Pget_chunk_cache(dapl_id, rdcc_nslots, rdcc_nbytes, rdcc_w0);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error getting dataset cache parameters");
}
return err;
}
inline herr_t h5p_set_chunk_cache(hid_t dapl_id,
size_t rdcc_nslots,
size_t rdcc_nbytes,
double rdcc_w0) {
herr_t err = H5Pset_chunk_cache(dapl_id, rdcc_nslots, rdcc_nbytes, rdcc_w0);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting dataset cache parameters");
}
return err;
}
inline herr_t h5p_set_create_intermediate_group(hid_t plist_id, unsigned crt_intmd) {
herr_t err = H5Pset_create_intermediate_group(plist_id, crt_intmd);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>(
"Error setting property for create intermediate groups");
}
return err;
}
inline herr_t h5p_get_create_intermediate_group(hid_t plist_id, unsigned* crt_intmd) {
herr_t err = H5Pget_create_intermediate_group(plist_id, crt_intmd);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>(
"Error getting property for create intermediate groups");
}
return err;
}
#ifdef H5_HAVE_PARALLEL
inline herr_t h5p_set_dxpl_mpio(hid_t dxpl_id, H5FD_mpio_xfer_t xfer_mode) {
herr_t err = H5Pset_dxpl_mpio(dxpl_id, xfer_mode);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting H5Pset_dxpl_mpio.");
}
return err;
}
inline herr_t h5p_get_dxpl_mpio(hid_t dxpl_id, H5FD_mpio_xfer_t* xfer_mode) {
herr_t err = H5Pget_dxpl_mpio(dxpl_id, xfer_mode);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error getting H5Pset_dxpl_mpio.");
}
return err;
}
inline herr_t h5p_get_mpio_no_collective_cause(hid_t plist_id,
uint32_t* local_no_collective_cause,
uint32_t* global_no_collective_cause) {
herr_t err = H5Pget_mpio_no_collective_cause(plist_id,
local_no_collective_cause,
global_no_collective_cause);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Failed to check mpio_no_collective_cause.");
}
return err;
}
#endif
inline herr_t h5p_set_link_creation_order(hid_t plist_id, unsigned crt_order_flags) {
herr_t err = H5Pset_link_creation_order(plist_id, crt_order_flags);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>("Error setting LinkCreationOrder.");
}
return err;
}
inline herr_t h5p_get_link_creation_order(hid_t plist_id, unsigned* crt_order_flags) {
herr_t err = H5Pget_link_creation_order(plist_id, crt_order_flags);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>(
"Error getting property for link creation order");
}
return err;
}
inline herr_t h5p_get_attr_phase_change(hid_t plist_id,
unsigned* max_compact,
unsigned* min_dense) {
herr_t err = H5Pget_attr_phase_change(plist_id, max_compact, min_dense);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>(
"Error getting property for attribute phase change");
}
return err;
}
inline herr_t h5p_set_attr_phase_change(hid_t plist_id, unsigned max_compact, unsigned min_dense) {
herr_t err = H5Pset_attr_phase_change(plist_id, max_compact, min_dense);
if (err < 0) {
HDF5ErrMapper::ToException<PropertyException>(
"Error getting property for attribute phase change");
}
return err;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,42 @@
#pragma once
#include <H5Rpublic.h>
namespace HighFive {
namespace detail {
inline herr_t h5r_create(void* ref,
hid_t loc_id,
const char* name,
H5R_type_t ref_type,
hid_t space_id) {
herr_t err = H5Rcreate(ref, loc_id, name, ref_type, space_id);
if (err < 0) {
HDF5ErrMapper::ToException<ReferenceException>(
std::string("Unable to create the reference for \"") + name + "\":");
}
return err;
}
#if (H5Rdereference_vers == 2)
inline hid_t h5r_dereference(hid_t obj_id, hid_t oapl_id, H5R_type_t ref_type, const void* ref) {
hid_t hid = H5Rdereference(obj_id, oapl_id, ref_type, ref);
if (hid < 0) {
HDF5ErrMapper::ToException<ReferenceException>("Unable to dereference.");
}
return hid;
}
#else
inline hid_t h5r_dereference(hid_t dataset, H5R_type_t ref_type, const void* ref) {
hid_t hid = H5Rdereference(dataset, ref_type, ref);
if (hid < 0) {
HDF5ErrMapper::ToException<ReferenceException>("Unable to dereference.");
}
return hid;
}
#endif
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,136 @@
#pragma once
#include <H5Ipublic.h>
#include <H5Spublic.h>
namespace HighFive {
namespace detail {
inline hid_t h5s_create_simple(int rank, const hsize_t dims[], const hsize_t maxdims[]) {
hid_t space_id = H5Screate_simple(rank, dims, maxdims);
if (space_id == H5I_INVALID_HID) {
throw DataSpaceException("Unable to create simple dataspace");
}
return space_id;
}
inline hid_t h5s_create(H5S_class_t type) {
hid_t space_id = H5Screate(type);
if (space_id == H5I_INVALID_HID) {
throw DataSpaceException("Unable to create dataspace");
}
return space_id;
}
inline hid_t h5s_copy(hid_t space_id) {
hid_t copy_id = H5Scopy(space_id);
if (copy_id < 0) {
throw DataSpaceException("Unable to copy dataspace");
}
return copy_id;
}
inline herr_t h5s_select_none(hid_t spaceid) {
herr_t err = H5Sselect_none(spaceid);
if (err < 0) {
HDF5ErrMapper::ToException<DataSpaceException>("Unable to select None space");
}
return err;
}
inline herr_t h5s_select_hyperslab(hid_t space_id,
H5S_seloper_t op,
const hsize_t start[],
const hsize_t stride[],
const hsize_t count[],
const hsize_t block[]) {
herr_t err = H5Sselect_hyperslab(space_id, op, start, stride, count, block);
if (err < 0) {
HDF5ErrMapper::ToException<DataSpaceException>("Unable to select hyperslab");
}
return err;
}
inline hssize_t h5s_get_select_npoints(hid_t spaceid) {
hssize_t n_points = H5Sget_select_npoints(spaceid);
if (n_points < 0) {
HDF5ErrMapper::ToException<DataSpaceException>(
"Unable to get number of points in selection");
}
return n_points;
}
inline herr_t h5s_select_elements(hid_t space_id,
H5S_seloper_t op,
size_t num_elem,
const hsize_t* coord) {
herr_t err = H5Sselect_elements(space_id, op, num_elem, coord);
if (err < 0) {
HDF5ErrMapper::ToException<DataSpaceException>("Unable to select elements");
}
return err;
}
inline int h5s_get_simple_extent_ndims(hid_t space_id) {
int ndim = H5Sget_simple_extent_ndims(space_id);
if (ndim < 0) {
HDF5ErrMapper::ToException<DataSetException>(
"Unable to get number of dimensions of dataspace");
}
return ndim;
}
inline herr_t h5s_get_simple_extent_dims(hid_t space_id, hsize_t dims[], hsize_t maxdims[]) {
herr_t err = H5Sget_simple_extent_dims(space_id, dims, maxdims);
if (err < 0) {
HDF5ErrMapper::ToException<DataSetException>("Unable to get dimensions of dataspace");
}
return err;
}
inline hssize_t h5s_get_simple_extent_npoints(hid_t space_id) {
hssize_t nelements = H5Sget_simple_extent_npoints(space_id);
if (nelements < 0) {
HDF5ErrMapper::ToException<DataSetException>(
"Unable to get number of elements in dataspace");
}
return nelements;
}
inline H5S_class_t h5s_get_simple_extent_type(hid_t space_id) {
H5S_class_t cls = H5Sget_simple_extent_type(space_id);
if (cls == H5S_NO_CLASS) {
HDF5ErrMapper::ToException<DataSpaceException>("Unable to get class of simple dataspace.");
}
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

View File

@ -0,0 +1,230 @@
#pragma once
#include <H5Ipublic.h>
#include <H5Tpublic.h>
namespace HighFive {
namespace detail {
inline hid_t h5t_copy(hid_t original) {
auto copy = H5Tcopy(original);
if (copy == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataTypeException>("Error copying datatype.");
}
return copy;
}
inline hsize_t h5t_get_size(hid_t hid) {
hsize_t size = H5Tget_size(hid);
if (size == 0) {
HDF5ErrMapper::ToException<DataTypeException>("Error getting size of datatype.");
}
return size;
}
inline H5T_cset_t h5t_get_cset(hid_t hid) {
auto cset = H5Tget_cset(hid);
if (cset == H5T_CSET_ERROR) {
HDF5ErrMapper::ToException<DataTypeException>("Error getting cset of datatype.");
}
return cset;
}
inline H5T_str_t h5t_get_strpad(hid_t hid) {
auto strpad = H5Tget_strpad(hid);
if (strpad == H5T_STR_ERROR) {
HDF5ErrMapper::ToException<DataTypeException>("Error getting strpad of datatype.");
}
return strpad;
}
inline void h5t_set_size(hid_t hid, hsize_t size) {
if (H5Tset_size(hid, size) < 0) {
HDF5ErrMapper::ToException<DataTypeException>("Error setting size of datatype.");
}
}
inline void h5t_set_cset(hid_t hid, H5T_cset_t cset) {
if (H5Tset_cset(hid, cset) < 0) {
HDF5ErrMapper::ToException<DataTypeException>("Error setting cset of datatype.");
}
}
inline void h5t_set_strpad(hid_t hid, H5T_str_t strpad) {
if (H5Tset_strpad(hid, strpad) < 0) {
HDF5ErrMapper::ToException<DataTypeException>("Error setting strpad of datatype.");
}
}
inline int h5t_get_nmembers(hid_t hid) {
auto result = H5Tget_nmembers(hid);
if (result < 0) {
throw DataTypeException("Could not get members of compound datatype");
}
return result;
}
inline char* h5t_get_member_name(hid_t type_id, unsigned membno) {
char* name = H5Tget_member_name(type_id, membno);
if (name == nullptr) {
throw DataTypeException("Failed to get member names of compound datatype");
}
return name;
}
inline size_t h5t_get_member_offset(hid_t type_id, unsigned membno) {
// Note, this function is peculiar. On failure it returns 0, yet 0 is also
// what's returned on failure.
return H5Tget_member_offset(type_id, membno);
}
inline hid_t h5t_get_member_type(hid_t type_id, unsigned membno) {
hid_t member_id = H5Tget_member_type(type_id, membno);
if (member_id < 0) {
throw DataTypeException("Failed to get member type of compound datatype");
}
return member_id;
}
#if H5_VERSION_GE(1, 12, 0)
inline herr_t h5t_reclaim(hid_t type_id, hid_t space_id, hid_t plist_id, void* buf) {
herr_t err = H5Treclaim(type_id, space_id, plist_id, buf);
if (err < 0) {
throw DataTypeException("Failed to reclaim HDF5 internal memory");
}
return err;
}
#endif
inline H5T_class_t h5t_get_class(hid_t type_id) {
H5T_class_t class_id = H5Tget_class(type_id);
if (class_id == H5T_NO_CLASS) {
throw DataTypeException("Failed to get class of type");
}
return class_id;
}
inline htri_t h5t_equal(hid_t type1_id, hid_t type2_id) {
htri_t equal = H5Tequal(type1_id, type2_id);
if (equal < 0) {
throw DataTypeException("Failed to compare two datatypes");
}
return equal;
}
inline htri_t h5t_is_variable_str(hid_t type_id) {
htri_t is_variable = H5Tis_variable_str(type_id);
if (is_variable < 0) {
HDF5ErrMapper::ToException<DataTypeException>(
"Failed to check if string is variable length");
}
return is_variable;
}
inline herr_t h5t_set_fields(hid_t type_id,
size_t spos,
size_t epos,
size_t esize,
size_t mpos,
size_t msize) {
herr_t err = H5Tset_fields(type_id, spos, epos, esize, mpos, msize);
if (err < 0) {
HDF5ErrMapper::ToException<DataTypeException>(
"Failed to create custom floating point data type");
}
return err;
}
inline herr_t h5t_set_ebias(hid_t type_id, size_t ebias) {
herr_t err = H5Tset_ebias(type_id, ebias);
if (err < 0) {
HDF5ErrMapper::ToException<DataTypeException>(
"Failed to exponent bias of floating point data type");
}
return err;
}
inline hid_t h5t_create(H5T_class_t type, size_t size) {
hid_t type_id = H5Tcreate(type, size);
if (type_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataTypeException>("Failed to datatype");
}
return type_id;
}
inline herr_t h5t_insert(hid_t parent_id, const char* name, size_t offset, hid_t member_id) {
herr_t err = H5Tinsert(parent_id, name, offset, member_id);
if (err < 0) {
HDF5ErrMapper::ToException<DataTypeException>("Failed to not add new member to datatype");
}
return err;
}
inline herr_t h5t_commit2(hid_t loc_id,
const char* name,
hid_t type_id,
hid_t lcpl_id,
hid_t tcpl_id,
hid_t tapl_id) {
herr_t err = H5Tcommit2(loc_id, name, type_id, lcpl_id, tcpl_id, tapl_id);
if (err < 0) {
HDF5ErrMapper::ToException<DataTypeException>("Failed to commit datatype");
}
return err;
}
inline herr_t h5t_close(hid_t type_id) {
auto err = H5Tclose(type_id);
if (err < 0) {
HDF5ErrMapper::ToException<DataTypeException>("Failed to close datatype");
}
return err;
}
inline hid_t h5t_enum_create(hid_t base_id) {
hid_t type_id = H5Tenum_create(base_id);
if (type_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataTypeException>("Failed to create new enum datatype");
}
return type_id;
}
inline herr_t h5t_enum_insert(hid_t type, const char* name, const void* value) {
herr_t err = H5Tenum_insert(type, name, value);
if (err < 0) {
HDF5ErrMapper::ToException<DataTypeException>(
"Failed to add new member to this enum datatype");
}
return err;
}
inline hid_t h5t_open2(hid_t loc_id, const char* name, hid_t tapl_id) {
hid_t datatype_id = H5Topen2(loc_id, name, tapl_id);
if (datatype_id == H5I_INVALID_HID) {
HDF5ErrMapper::ToException<DataTypeException>(
std::string("Unable to open the datatype \"") + name + "\":");
}
return datatype_id;
}
} // namespace detail
} // namespace HighFive

View File

@ -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

View File

@ -0,0 +1,54 @@
/*
* Copyright (c), 2024, BlueBrain Project, EPFL
*
* 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)
*
*/
#pragma once
#include <vector>
#include "../H5Exception.hpp"
namespace HighFive {
namespace detail {
/// \brief Squeeze `axes` from `dims`.
///
/// An axis can only be squeezed if it's dimension is `1`. The elements of
/// `axes` must be in the range `0, ..., dims.size()` (exclusive) and don't
/// have to be sorted.
///
/// Example:
/// squeeze({1, 3, 2, 1}, {0, 3}) == {3, 2}
inline std::vector<size_t> squeeze(const std::vector<size_t>& dims,
const std::vector<size_t>& axes) {
auto n_dims = dims.size();
auto mask = std::vector<bool>(n_dims, false);
for (size_t i = 0; i < axes.size(); ++i) {
if (axes[i] >= n_dims) {
throw Exception("Out of range: axes[" + std::to_string(i) +
"] == " + std::to_string(axes[i]) + " >= " + std::to_string(n_dims));
}
mask[axes[i]] = true;
}
auto squeezed_dims = std::vector<size_t>{};
for (size_t i = 0; i < n_dims; ++i) {
if (!mask[i]) {
squeezed_dims.push_back(dims[i]);
} else {
if (dims[i] != 1) {
throw Exception("Squeezing non-unity axis: axes[" + std::to_string(i) +
"] = " + std::to_string(axes[i]));
}
}
}
return squeezed_dims;
}
} // namespace detail
} // namespace HighFive

View File

@ -0,0 +1,14 @@
#pragma once
#include <H5Tpublic.h>
namespace HighFive {
enum class StringPadding : std::underlying_type<H5T_str_t>::type {
NullTerminated = H5T_STR_NULLTERM,
NullPadded = H5T_STR_NULLPAD,
SpacePadded = H5T_STR_SPACEPAD
};
}

View File

@ -0,0 +1,22 @@
// clang-format off
#if HIGHFIVE_XTENSOR_HEADER_VERSION == 0
#if __cplusplus >= 201703L
#if __has_include(<xtensor/xtensor.hpp>)
#define HIGHFIVE_XTENSOR_HEADER_VERSION 1
#elif __has_include(<xtensor/containers/xtensor.hpp>)
#define HIGHFIVE_XTENSOR_HEADER_VERSION 2
#else
#error "Unable to guess HIGHFIVE_XTENSOR_HEADER_VERSION. Please set manually."
#endif
#elif __cplusplus == 201402L
// XTensor 0.26 and newer require C++17. Hence, if we have C++14, only
// `HIGHFIVE_XTENSOR_HEADER_VERSION == 1` makes sense.
#define HIGHFIVE_XTENSOR_HEADER_VERSION 1
#elif defined(_MSC_VER) && __cplusplus == 199711L
#error \
"Use /Zc:__cplusplus to make MSVC set __cplusplus correctly or HIGHFIVE_XTENSOR_HEADER_VERSION to skip xtensor version deduction."
#else
#error "HighFive requires C++14 or newer."
#endif
#endif
// clang-format on

View File

@ -0,0 +1,4 @@
#pragma once
#include "boost_ublas.hpp"
#include "boost_multi_array.hpp"

View File

@ -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

View File

@ -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

View File

@ -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

161
include/highfive/eigen.hpp Normal file
View File

@ -0,0 +1,161 @@
#pragma once
#include "bits/H5Inspector_decl.hpp"
#include "H5Exception.hpp"
#include <Eigen/Core>
#include <Eigen/Dense>
namespace HighFive {
namespace details {
template <class EigenType>
struct eigen_inspector {
using type = EigenType;
using value_type = typename EigenType::Scalar;
using base_type = typename inspector<value_type>::base_type;
using hdf5_type = base_type;
static_assert(int(EigenType::ColsAtCompileTime) == int(EigenType::MaxColsAtCompileTime),
"Padding isn't supported.");
static_assert(int(EigenType::RowsAtCompileTime) == int(EigenType::MaxRowsAtCompileTime),
"Padding isn't supported.");
static constexpr bool is_row_major() {
return EigenType::ColsAtCompileTime == 1 || EigenType::RowsAtCompileTime == 1 ||
EigenType::IsRowMajor;
}
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 = is_row_major() &&
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) {
std::vector<size_t> sizes{static_cast<size_t>(val.rows()), static_cast<size_t>(val.cols())};
auto s = inspector<value_type>::getDimensions(val.data()[0]);
sizes.insert(sizes.end(), s.begin(), s.end());
return sizes;
}
static void prepare(type& val, const std::vector<size_t>& dims) {
if (dims[0] != static_cast<size_t>(val.rows()) ||
dims[1] != static_cast<size_t>(val.cols())) {
val.resize(static_cast<typename type::Index>(dims[0]),
static_cast<typename type::Index>(dims[1]));
}
}
static hdf5_type* data(type& val) {
if (!is_trivially_copyable) {
throw DataSetException("Invalid used of `inspector<Eigen::Matrix<...>>::data`.");
}
return inspector<value_type>::data(*val.data());
}
static const hdf5_type* data(const type& val) {
if (!is_trivially_copyable) {
throw DataSetException("Invalid used of `inspector<Eigen::Matrix<...>>::data`.");
}
return inspector<value_type>::data(*val.data());
}
static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* m) {
Eigen::Index n_rows = val.rows();
Eigen::Index n_cols = val.cols();
auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end());
auto subsize = compute_total_size(subdims);
for (Eigen::Index i = 0; i < n_rows; ++i) {
for (Eigen::Index j = 0; j < n_cols; ++j) {
inspector<value_type>::serialize(val(i, j), dims, m);
m += subsize;
}
}
}
static void unserialize(const hdf5_type* vec_align,
const std::vector<size_t>& dims,
type& val) {
if (dims.size() < 2) {
std::ostringstream os;
os << "Impossible to pair DataSet with " << dims.size()
<< " dimensions into an eigen-matrix.";
throw DataSpaceException(os.str());
}
auto n_rows = static_cast<Eigen::Index>(dims[0]);
auto n_cols = static_cast<Eigen::Index>(dims[1]);
auto subdims = std::vector<size_t>(dims.begin() + ndim, dims.end());
auto subsize = compute_total_size(subdims);
for (Eigen::Index i = 0; i < n_rows; ++i) {
for (Eigen::Index j = 0; j < n_cols; ++j) {
inspector<value_type>::unserialize(vec_align, subdims, val(i, j));
vec_align += subsize;
}
}
}
};
template <typename T, int M, int N, int Options>
struct inspector<Eigen::Matrix<T, M, N, Options>>
: public eigen_inspector<Eigen::Matrix<T, M, N, Options>> {
private:
using super = eigen_inspector<Eigen::Matrix<T, M, N, Options>>;
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;
};
template <typename T, int M, int N, int Options>
struct inspector<Eigen::Array<T, M, N, Options>>
: public eigen_inspector<Eigen::Array<T, M, N, Options>> {
private:
using super = eigen_inspector<Eigen::Array<T, M, N, Options>>;
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;
};
template <typename PlainObjectType, int MapOptions>
struct inspector<Eigen::Map<PlainObjectType, MapOptions>>
: public eigen_inspector<Eigen::Map<PlainObjectType, MapOptions>> {
private:
using super = eigen_inspector<Eigen::Map<PlainObjectType, MapOptions>>;
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;
static void prepare(type& val, const std::vector<size_t>& dims) {
if (dims[0] != static_cast<size_t>(val.rows()) ||
dims[1] != static_cast<size_t>(val.cols())) {
throw DataSetException("Eigen::Map has invalid shape and can't be resized.");
}
}
};
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,149 @@
#pragma once
#include "../bits/H5Inspector_decl.hpp"
#include "../H5Exception.hpp"
#include <opencv2/opencv.hpp>
#include "../bits/convert_size_vector.hpp"
namespace HighFive {
namespace details {
template <class T>
struct inspector<cv::Mat_<T>> {
using type = cv::Mat_<T>;
using value_type = T;
using base_type = typename inspector<value_type>::base_type;
using hdf5_type = base_type;
static void assert_row_major(const type& type) {
// Documentation claims that Mat_ is always row-major. However, it
// could be padded. The steps/strides are in bytes.
int rank = type.dims;
size_t ld = sizeof(T);
for (int i = rank - 1; i >= 0; --i) {
if (static_cast<size_t>(type.step[i]) != ld) {
throw DataSetException("Padded cv::Mat_ are not supported.");
}
ld *= static_cast<size_t>(type.size[i]);
}
}
static constexpr size_t min_ndim = 2 + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = 1024 + inspector<value_type>::max_ndim;
// HighFive doesn't support padded OpenCV arrays. Therefore, pretend
// that they themselves are trivially copyable. And error out if the
// assumption is violated.
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 min_ndim;
} else {
return static_cast<size_t>(val.dims) +
inspector<value_type>::getRank(getAnyElement(val));
}
}
static const T& getAnyElement(const type& val) {
return *reinterpret_cast<T const*>(val.data);
}
static T& getAnyElement(type& val) {
return *reinterpret_cast<T*>(val.data);
}
static size_t getLocalRank(const type& val) {
return static_cast<size_t>(val.dims);
}
static std::vector<size_t> getDimensions(const type& val) {
auto local_rank = getLocalRank(val);
auto rank = getRank(val);
std::vector<size_t> dims(rank, 1ul);
if (val.empty()) {
dims[0] = 0ul;
dims[1] = 1ul;
return dims;
}
for (size_t i = 0; i < local_rank; ++i) {
dims[i] = static_cast<size_t>(val.size[static_cast<int>(i)]);
}
auto s = inspector<value_type>::getDimensions(getAnyElement(val));
std::copy(s.cbegin(), s.cend(), dims.begin() + static_cast<int>(local_rank));
return dims;
}
static void prepare(type& val, const std::vector<size_t>& dims) {
auto subdims = detail::convertSizeVector<int>(dims);
val.create(static_cast<int>(subdims.size()), subdims.data());
}
static hdf5_type* data(type& val) {
assert_row_major(val);
if (!is_trivially_copyable) {
throw DataSetException("Invalid used of `inspector<Eigen::Matrix<...>>::data`.");
}
if (val.empty()) {
return nullptr;
}
return inspector<value_type>::data(getAnyElement(val));
}
static const hdf5_type* data(const type& val) {
assert_row_major(val);
if (!is_trivially_copyable) {
throw DataSetException("Invalid used of `inspector<Eigen::Matrix<...>>::data`.");
}
if (val.empty()) {
return nullptr;
}
return inspector<value_type>::data(getAnyElement(val));
}
static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* m) {
if (val.empty()) {
return;
}
auto local_rank = val.dims;
auto subdims = std::vector<size_t>(dims.begin() + local_rank, dims.end());
auto subsize = compute_total_size(subdims);
for (auto it = val.begin(); it != val.end(); ++it) {
inspector<value_type>::serialize(*it, subdims, m);
m += subsize;
}
}
static void unserialize(const hdf5_type* vec_align,
const std::vector<size_t>& dims,
type& val) {
auto local_rank = val.dims;
auto subdims = std::vector<size_t>(dims.begin() + local_rank, dims.end());
auto subsize = compute_total_size(subdims);
for (auto it = val.begin(); it != val.end(); ++it) {
inspector<value_type>::unserialize(vec_align, subdims, *it);
vec_align += subsize;
}
}
};
} // namespace details
} // namespace HighFive

View File

@ -0,0 +1,117 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include "../H5Easy.hpp"
#include "H5Easy_misc.hpp"
#include "H5Easy_scalar.hpp"
#ifdef H5_USE_EIGEN
#include "../eigen.hpp"
namespace H5Easy {
namespace detail {
template <typename T>
struct io_impl<T, typename std::enable_if<std::is_base_of<Eigen::DenseBase<T>, T>::value>::type> {
using EigenIndex = Eigen::DenseIndex;
// When creating a dataset for an Eigen object, the shape of the dataset is
// 1D for vectors. (legacy reasons)
inline static std::vector<size_t> file_shape(const T& data) {
if (std::decay<T>::type::RowsAtCompileTime == 1) {
return {static_cast<size_t>(data.cols())};
}
if (std::decay<T>::type::ColsAtCompileTime == 1) {
return {static_cast<size_t>(data.rows())};
}
return inspector<T>::getDimensions(data);
}
// The shape of an Eigen object as used in HighFive core.
inline static std::vector<size_t> mem_shape(const T& data) {
return inspector<T>::getDimensions(data);
}
// The shape of an Eigen object as used in HighFive core.
template <class D>
inline static std::vector<size_t> mem_shape(const File& file,
const std::string& path,
const D& dataset) {
std::vector<size_t> dims = dataset.getDimensions();
if (dims.size() == 1 && T::RowsAtCompileTime == 1) {
return std::vector<size_t>{1, dims[0]};
}
if (dims.size() == 1 && T::ColsAtCompileTime == 1) {
return std::vector<size_t>{dims[0], 1};
}
if (dims.size() == 2) {
return dims;
}
throw detail::error(file, path, "H5Easy::load: Inconsistent rank");
}
inline static DataSet dump(File& file,
const std::string& path,
const T& data,
const DumpOptions& options) {
using value_type = typename std::decay<T>::type::Scalar;
std::vector<size_t> file_dims = file_shape(data);
std::vector<size_t> mem_dims = mem_shape(data);
DataSet dataset = initDataset<value_type>(file, path, file_dims, options);
dataset.reshapeMemSpace(mem_dims).write(data);
if (options.flush()) {
file.flush();
}
return dataset;
}
inline static T load(const File& file, const std::string& path) {
DataSet dataset = file.getDataSet(path);
std::vector<size_t> dims = mem_shape(file, path, dataset);
return dataset.reshapeMemSpace(dims).template read<T>();
}
inline static Attribute dumpAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
const DumpOptions& options) {
using value_type = typename std::decay<T>::type::Scalar;
std::vector<size_t> file_dims = file_shape(data);
std::vector<size_t> mem_dims = mem_shape(data);
Attribute attribute = initAttribute<value_type>(file, path, key, file_dims, options);
attribute.reshapeMemSpace(mem_dims).write(data);
if (options.flush()) {
file.flush();
}
return attribute;
}
inline static T loadAttribute(const File& file,
const std::string& path,
const std::string& key) {
DataSet dataset = file.getDataSet(path);
Attribute attribute = dataset.getAttribute(key);
DataSpace dataspace = attribute.getSpace();
std::vector<size_t> dims = mem_shape(file, path, dataspace);
return attribute.reshapeMemSpace(dims).template read<T>();
}
};
} // namespace detail
} // namespace H5Easy
#endif // H5_USE_EIGEN

View File

@ -0,0 +1,179 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include "../H5Easy.hpp"
namespace H5Easy {
namespace detail {
// Generate error-stream and return "Exception" (not yet thrown).
inline Exception error(const File& file, const std::string& path, const std::string& message) {
std::ostringstream ss;
ss << message << std::endl
<< "Path: " << path << std::endl
<< "Filename: " << file.getName() << std::endl;
return Exception(ss.str());
}
// Generate specific dump error
inline Exception dump_error(File& file, const std::string& path) {
if (file.getObjectType(path) == ObjectType::Dataset) {
return error(file,
path,
"H5Easy: Dataset already exists, dump with H5Easy::DumpMode::Overwrite "
"to overwrite (with an array of the same shape).");
} else {
return error(
file,
path,
"H5Easy: path exists, but does not correspond to a Dataset. Dump not possible.");
}
}
// get a opened DataSet: nd-array
template <class T>
inline DataSet initDataset(File& file,
const std::string& path,
const std::vector<size_t>& shape,
const DumpOptions& options) {
if (!file.exist(path)) {
if (!options.compress() && !options.isChunked()) {
return file.createDataSet<T>(path, DataSpace(shape), {}, {}, true);
} else {
std::vector<hsize_t> chunks(shape.begin(), shape.end());
if (options.isChunked()) {
chunks = options.getChunkSize();
if (chunks.size() != shape.size()) {
throw error(file, path, "H5Easy::dump: Incorrect rank ChunkSize");
}
}
DataSetCreateProps props;
props.add(Chunking(chunks));
if (options.compress()) {
props.add(Shuffle());
props.add(Deflate(options.getCompressionLevel()));
}
return file.createDataSet<T>(path, DataSpace(shape), props, {}, true);
}
} else if (options.overwrite() && file.getObjectType(path) == ObjectType::Dataset) {
DataSet dataset = file.getDataSet(path);
if (dataset.getDimensions() != shape) {
throw error(file, path, "H5Easy::dump: Inconsistent dimensions");
}
return dataset;
}
throw dump_error(file, path);
}
// get a opened DataSet: scalar
template <class T>
inline DataSet initScalarDataset(File& file,
const std::string& path,
const T& data,
const DumpOptions& options) {
if (!file.exist(path)) {
return file.createDataSet<T>(path, DataSpace::From(data), {}, {}, true);
} else if (options.overwrite() && file.getObjectType(path) == ObjectType::Dataset) {
DataSet dataset = file.getDataSet(path);
if (dataset.getElementCount() != 1) {
throw error(file, path, "H5Easy::dump: Existing field not a scalar");
}
return dataset;
}
throw dump_error(file, path);
}
template <class File, class F>
auto apply_attr_func_impl(File& file, const std::string& path, F f) {
auto type = file.getObjectType(path);
if (type == ObjectType::Group) {
auto group = file.getGroup(path);
return f(group);
} else if (type == ObjectType::Dataset) {
auto dataset = file.getDataSet(path);
return f(dataset);
} else {
throw error(file, path, "path is not the root, a group or a dataset.");
}
}
template <class F>
auto apply_attr_func(const H5Easy::File& file, const std::string& path, F f) {
return apply_attr_func_impl(file, path, f);
}
template <class F>
auto apply_attr_func(H5Easy::File& file, const std::string& path, F f) {
return apply_attr_func_impl(file, path, f);
}
// get a opened Attribute: nd-array
template <class T>
inline Attribute initAttribute(File& file,
const std::string& path,
const std::string& key,
const std::vector<size_t>& shape,
const DumpOptions& options) {
auto get_attribute = [&](auto& obj) {
if (!obj.hasAttribute(key)) {
return obj.template createAttribute<T>(key, DataSpace(shape));
} else if (options.overwrite()) {
Attribute attribute = obj.getAttribute(key);
DataSpace dataspace = attribute.getSpace();
if (dataspace.getDimensions() != shape) {
throw error(file, path, "H5Easy::dumpAttribute: Inconsistent dimensions");
}
return attribute;
}
throw error(file,
path,
"H5Easy: Attribute exists, overwrite with H5Easy::DumpMode::Overwrite.");
};
if (!file.exist(path)) {
throw error(file, path, "H5Easy::dumpAttribute: path does not exist");
}
return apply_attr_func(file, path, get_attribute);
}
// get a opened Attribute: scalar
template <class T>
inline Attribute initScalarAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
const DumpOptions& options) {
auto get_attribute = [&](auto& obj) {
if (!obj.hasAttribute(key)) {
return obj.template createAttribute<T>(key, DataSpace::From(data));
} else if (options.overwrite()) {
Attribute attribute = obj.getAttribute(key);
DataSpace dataspace = attribute.getSpace();
if (dataspace.getElementCount() != 1) {
throw error(file, path, "H5Easy::dumpAttribute: Existing field not a scalar");
}
return attribute;
}
throw error(file,
path,
"H5Easy: Attribute exists, overwrite with H5Easy::DumpMode::Overwrite.");
};
if (!file.exist(path)) {
throw error(file, path, "H5Easy::dumpAttribute: path does not exist");
}
apply_attr_func(file, path, get_attribute);
}
} // namespace detail
} // namespace H5Easy

View File

@ -0,0 +1,170 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include "../H5Easy.hpp"
namespace H5Easy {
inline Compression::Compression(bool enable) {
if (enable) {
m_compression_level = 9;
} else {
m_compression_level = 0;
}
}
template <class T>
inline Compression::Compression(T level)
: m_compression_level(static_cast<unsigned>(level)) {}
inline unsigned Compression::get() const {
return m_compression_level;
}
inline void DumpOptions::set(DumpMode mode) {
m_overwrite = static_cast<bool>(mode);
}
inline void DumpOptions::set(Flush mode) {
m_flush = static_cast<bool>(mode);
}
inline void DumpOptions::set(const Compression& level) {
m_compression_level = level.get();
}
template <class T, class... Args>
inline void DumpOptions::set(T arg, Args... args) {
set(arg);
set(args...);
}
template <class T>
inline void DumpOptions::setChunkSize(const std::vector<T>& shape) {
m_chunk_size = std::vector<hsize_t>(shape.begin(), shape.end());
}
inline void DumpOptions::setChunkSize(std::initializer_list<size_t> shape) {
m_chunk_size = std::vector<hsize_t>(shape.begin(), shape.end());
}
inline bool DumpOptions::overwrite() const {
return m_overwrite;
}
inline bool DumpOptions::flush() const {
return m_flush;
}
inline bool DumpOptions::compress() const {
return m_compression_level > 0;
}
inline unsigned DumpOptions::getCompressionLevel() const {
return m_compression_level;
}
inline bool DumpOptions::isChunked() const {
return m_chunk_size.size() > 0;
}
inline std::vector<hsize_t> DumpOptions::getChunkSize() const {
return m_chunk_size;
}
inline size_t getSize(const File& file, const std::string& path) {
return file.getDataSet(path).getElementCount();
}
inline std::vector<size_t> getShape(const File& file, const std::string& path) {
return file.getDataSet(path).getDimensions();
}
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const DumpOptions& options) {
return detail::io_impl<T>::dump(file, path, data, options);
}
template <class T>
inline DataSet dump(File& file, const std::string& path, const T& data, DumpMode mode) {
return detail::io_impl<T>::dump(file, path, data, DumpOptions(mode));
}
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::vector<size_t>& idx,
const DumpOptions& options) {
return detail::io_impl<T>::dump_extend(file, path, data, idx, options);
}
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::initializer_list<size_t>& idx,
const DumpOptions& options) {
return detail::io_impl<T>::dump_extend(file, path, data, idx, options);
}
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::vector<size_t>& idx) {
return detail::io_impl<T>::dump_extend(file, path, data, idx, DumpOptions());
}
template <class T>
inline DataSet dump(File& file,
const std::string& path,
const T& data,
const std::initializer_list<size_t>& idx) {
return detail::io_impl<T>::dump_extend(file, path, data, idx, DumpOptions());
}
template <class T>
inline T load(const File& file, const std::string& path, const std::vector<size_t>& idx) {
return detail::io_impl<T>::load_part(file, path, idx);
}
template <class T>
inline T load(const File& file, const std::string& path) {
return detail::io_impl<T>::load(file, path);
}
template <class T>
inline Attribute dumpAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
DumpMode mode) {
return detail::io_impl<T>::dumpAttribute(file, path, key, data, DumpOptions(mode));
}
template <class T>
inline Attribute dumpAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
const DumpOptions& options) {
return detail::io_impl<T>::dumpAttribute(file, path, key, data, options);
}
template <class T>
inline T loadAttribute(const File& file, const std::string& path, const std::string& key) {
return detail::io_impl<T>::loadAttribute(file, path, key);
}
} // namespace H5Easy

View File

@ -0,0 +1,88 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include "../H5Easy.hpp"
#include "H5Easy_misc.hpp"
#include "default_io_impl.hpp"
namespace H5Easy {
namespace detail {
/*
Base template for partial specialization: the fallback if specialized templates don't match.
Used e.g. for scalars.
*/
template <typename T, typename = void>
struct io_impl: public default_io_impl<T> {
inline static DataSet dump_extend(File& file,
const std::string& path,
const T& data,
const std::vector<size_t>& idx,
const DumpOptions& options) {
std::vector<size_t> ones(idx.size(), 1);
if (file.exist(path)) {
DataSet dataset = file.getDataSet(path);
std::vector<size_t> dims = dataset.getDimensions();
std::vector<size_t> shape = dims;
if (dims.size() != idx.size()) {
throw detail::error(
file,
path,
"H5Easy::dump: Dimension of the index and the existing field do not match");
}
for (size_t i = 0; i < dims.size(); ++i) {
shape[i] = std::max(dims[i], idx[i] + 1);
}
if (shape != dims) {
dataset.resize(shape);
}
dataset.select(idx, ones).write(data);
if (options.flush()) {
file.flush();
}
return dataset;
}
const size_t unlim = DataSpace::UNLIMITED;
std::vector<size_t> unlim_shape(idx.size(), unlim);
std::vector<hsize_t> chunks(idx.size(), 10);
if (options.isChunked()) {
chunks = options.getChunkSize();
if (chunks.size() != idx.size()) {
throw error(file, path, "H5Easy::dump: Incorrect dimension ChunkSize");
}
}
std::vector<size_t> shape(idx.size());
for (size_t i = 0; i < idx.size(); ++i) {
shape[i] = idx[i] + 1;
}
DataSpace dataspace = DataSpace(shape, unlim_shape);
DataSetCreateProps props;
props.add(Chunking(chunks));
DataSet dataset = file.createDataSet(path, dataspace, AtomicType<T>(), props, {}, true);
dataset.select(idx, ones).write(data);
if (options.flush()) {
file.flush();
}
return dataset;
}
inline static T load_part(const File& file,
const std::string& path,
const std::vector<size_t>& idx) {
std::vector<size_t> ones(idx.size(), 1);
return file.getDataSet(path).select(idx, ones).read<T>();
}
};
} // namespace detail
} // namespace H5Easy

View File

@ -0,0 +1,69 @@
/*
* Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
*
* 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)
*
*/
#pragma once
#include "../H5Easy.hpp"
#include "H5Easy_misc.hpp"
#include "H5Easy_scalar.hpp"
namespace H5Easy {
namespace detail {
using HighFive::details::inspector;
template <typename T>
struct default_io_impl {
inline static std::vector<size_t> shape(const T& data) {
return inspector<T>::getDimensions(data);
}
inline static DataSet dump(File& file,
const std::string& path,
const T& data,
const DumpOptions& options) {
using value_type = typename inspector<T>::base_type;
DataSet dataset = initDataset<value_type>(file, path, shape(data), options);
dataset.write(data);
if (options.flush()) {
file.flush();
}
return dataset;
}
inline static T load(const File& file, const std::string& path) {
return file.getDataSet(path).read<T>();
}
inline static Attribute dumpAttribute(File& file,
const std::string& path,
const std::string& key,
const T& data,
const DumpOptions& options) {
using value_type = typename inspector<T>::base_type;
Attribute attribute = initAttribute<value_type>(file, path, key, shape(data), options);
attribute.write(data);
if (options.flush()) {
file.flush();
}
return attribute;
}
inline static T loadAttribute(const File& file,
const std::string& path,
const std::string& key) {
auto read_attribute = [&key](const auto& obj) {
return obj.getAttribute(key).template read<T>();
};
return apply_attr_func(file, path, read_attribute);
}
};
} // namespace detail
} // namespace H5Easy

View File

@ -0,0 +1,19 @@
#pragma once
#include <half.hpp>
namespace HighFive {
using float16_t = half_float::half;
template <>
inline AtomicType<float16_t>::AtomicType() {
_hid = detail::h5t_copy(H5T_NATIVE_FLOAT);
// Sign position, exponent position, exponent size, mantissa position, mantissa size
detail::h5t_set_fields(_hid, 15, 10, 5, 0, 10);
// Total datatype size (in bytes)
detail::h5t_set_size(_hid, 2);
// Floating point exponent bias
detail::h5t_set_ebias(_hid, 15);
}
} // namespace HighFive

View File

@ -0,0 +1,13 @@
#pragma once
#include <highfive/H5Attribute.hpp>
#include <highfive/H5DataSet.hpp>
#include <highfive/H5DataSpace.hpp>
#include <highfive/H5DataType.hpp>
#include <highfive/H5File.hpp>
#include <highfive/H5Group.hpp>
#include <highfive/H5PropertyList.hpp>
#include <highfive/H5Reference.hpp>
#include <highfive/H5Selection.hpp>
#include <highfive/H5Utility.hpp>
#include <highfive/H5Version.hpp>

33
include/highfive/span.hpp Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Blue Brain Project
*
* 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)
*
*/
#pragma once
#include "bits/H5Inspector_decl.hpp"
#include "bits/inspector_stl_span_misc.hpp"
#include <span>
namespace HighFive {
namespace details {
template <class T, std::size_t Extent>
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
} // namespace HighFive

View File

@ -0,0 +1,221 @@
#pragma once
#include "bits/H5Inspector_decl.hpp"
#include "H5Exception.hpp"
#include "bits/xtensor_header_version.hpp"
#if HIGHFIVE_XTENSOR_HEADER_VERSION == 1
#include <xtensor/xtensor.hpp>
#include <xtensor/xarray.hpp>
#include <xtensor/xadapt.hpp>
#elif HIGHFIVE_XTENSOR_HEADER_VERSION == 2
#include <xtensor/containers/xtensor.hpp>
#include <xtensor/containers/xarray.hpp>
#include <xtensor/containers/xadapt.hpp>
#else
#error "Set HIGHFIVE_XTENSOR_HEADER_VERSION to `1` for pre 0.26; `2` otherwise."
#endif
namespace HighFive {
namespace details {
template <class XTensor>
struct xtensor_get_rank;
template <typename T, size_t N, xt::layout_type L>
struct xtensor_get_rank<xt::xtensor<T, N, L>> {
static constexpr size_t value = N;
};
template <class EC, size_t N, xt::layout_type L, class Tag>
struct xtensor_get_rank<xt::xtensor_adaptor<EC, N, L, Tag>> {
static constexpr size_t value = N;
};
template <class Derived, class XTensorType, xt::layout_type L>
struct xtensor_inspector_base {
using type = XTensorType;
using value_type = typename type::value_type;
using base_type = typename inspector<value_type>::base_type;
using hdf5_type = base_type;
static_assert(std::is_same<value_type, base_type>::value,
"HighFive's XTensor support only works for scalar elements.");
static constexpr bool IsConstExprRowMajor = L == xt::layout_type::row_major;
static constexpr bool is_trivially_copyable = IsConstExprRowMajor &&
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) {
// Non-scalar elements are not supported.
return val.shape().size();
}
static const value_type& getAnyElement(const type& val) {
return val.unchecked(0);
}
static value_type& getAnyElement(type& val) {
return val.unchecked(0);
}
static std::vector<size_t> getDimensions(const type& val) {
auto shape = val.shape();
return {shape.begin(), shape.end()};
}
static void prepare(type& val, const std::vector<size_t>& dims) {
val.resize(Derived::shapeFromDims(dims));
}
static hdf5_type* data(type& val) {
if (!is_trivially_copyable) {
throw DataSetException("Invalid used of `inspector<XTensor>::data`.");
}
if (val.size() == 0) {
return nullptr;
}
return inspector<value_type>::data(getAnyElement(val));
}
static const hdf5_type* data(const type& val) {
if (!is_trivially_copyable) {
throw DataSetException("Invalid used of `inspector<XTensor>::data`.");
}
if (val.size() == 0) {
return nullptr;
}
return inspector<value_type>::data(getAnyElement(val));
}
static void serialize(const type& val, const std::vector<size_t>& dims, hdf5_type* m) {
// since we only support scalar types we know all dims belong to us.
size_t size = compute_total_size(dims);
xt::adapt(m, size, xt::no_ownership(), dims) = val;
}
static void unserialize(const hdf5_type* vec_align,
const std::vector<size_t>& dims,
type& val) {
// since we only support scalar types we know all dims belong to us.
size_t size = compute_total_size(dims);
val = xt::adapt(vec_align, size, xt::no_ownership(), dims);
}
};
template <class XTensorType, xt::layout_type L>
struct xtensor_inspector
: public xtensor_inspector_base<xtensor_inspector<XTensorType, L>, XTensorType, L> {
private:
using super = xtensor_inspector_base<xtensor_inspector<XTensorType, L>, XTensorType, L>;
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;
static constexpr size_t ndim = xtensor_get_rank<XTensorType>::value;
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 std::array<size_t, ndim> shapeFromDims(const std::vector<size_t>& dims) {
std::array<size_t, ndim> shape;
std::copy(dims.cbegin(), dims.cend(), shape.begin());
return shape;
}
};
template <class XArrayType, xt::layout_type L>
struct xarray_inspector
: public xtensor_inspector_base<xarray_inspector<XArrayType, L>, XArrayType, L> {
private:
using super = xtensor_inspector_base<xarray_inspector<XArrayType, L>, XArrayType, L>;
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;
static constexpr size_t min_ndim = 0 + inspector<value_type>::min_ndim;
static constexpr size_t max_ndim = 1024 + inspector<value_type>::max_ndim;
static const std::vector<size_t>& shapeFromDims(const std::vector<size_t>& dims) {
return dims;
}
};
template <typename T, size_t N, xt::layout_type L>
struct inspector<xt::xtensor<T, N, L>>: public xtensor_inspector<xt::xtensor<T, N, L>, L> {
private:
using super = xtensor_inspector<xt::xtensor<T, N, L>, L>;
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;
};
template <typename T, xt::layout_type L>
struct inspector<xt::xarray<T, L>>: public xarray_inspector<xt::xarray<T, L>, L> {
private:
using super = xarray_inspector<xt::xarray<T, L>, L>;
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;
};
template <typename CT, class... S>
struct inspector<xt::xview<CT, S...>>
: public xarray_inspector<xt::xview<CT, S...>, xt::layout_type::any> {
private:
using super = xarray_inspector<xt::xview<CT, S...>, xt::layout_type::any>;
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;
};
template <class EC, xt::layout_type L, class SC, class Tag>
struct inspector<xt::xarray_adaptor<EC, L, SC, Tag>>
: public xarray_inspector<xt::xarray_adaptor<EC, L, SC, Tag>, xt::layout_type::any> {
private:
using super = xarray_inspector<xt::xarray_adaptor<EC, L, SC, Tag>, xt::layout_type::any>;
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;
};
template <class EC, size_t N, xt::layout_type L, class Tag>
struct inspector<xt::xtensor_adaptor<EC, N, L, Tag>>
: public xtensor_inspector<xt::xtensor_adaptor<EC, N, L, Tag>, xt::layout_type::any> {
private:
using super = xtensor_inspector<xt::xtensor_adaptor<EC, N, L, Tag>, xt::layout_type::any>;
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

View File

@ -0,0 +1,28 @@
include(CMakeFindDependencyMacro)
if(NOT DEFINED HIGHFIVE_FIND_HDF5)
set(HIGHFIVE_FIND_HDF5 On)
endif()
if(HIGHFIVE_FIND_HDF5)
find_dependency(HDF5)
endif()
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)
endif()
add_library(HighFive ALIAS HighFive::HighFive)
add_library(HighFiveInclude ALIAS HighFive::Include)
endif()
if(HIGHFIVE_XTENSOR_HEADER_VERSION)
target_compile_definitions(HighFive::HighFive PUBLIC
HIGHFIVE_XTENSOR_HEADER_VERSION=${HIGHFIVE_XTENSOR_HEADER_VERSION}
)
endif()

View File

@ -0,0 +1,48 @@
# This is a basic version file for the Config-mode of find_package().
# It is used by write_basic_package_version_file() as input file for configure_file()
# to create a version-file which can be installed along a config.cmake file.
#
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
# the requested version string are exactly the same and it sets
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version.
# The variable CVF_VERSION must be set before calling configure_file().
set(PACKAGE_VERSION "3.0.0")
if (PACKAGE_FIND_VERSION_RANGE)
# Package version must be in the requested version range
if ((PACKAGE_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MIN)
OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_MAX)
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_GREATER_EQUAL PACKAGE_FIND_VERSION_MAX)))
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif()
else()
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
endif()
# if the installed project requested no architecture check, don't perform the check
if("FALSE")
return()
endif()
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
return()
endif()
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
math(EXPR installedBits "8 * 8")
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()

View File

@ -0,0 +1,105 @@
# Generated by CMake
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6)
message(FATAL_ERROR "CMake >= 2.6.0 required")
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 2.6...3.21)
#----------------------------------------------------------------
# Generated CMake target import file.
#----------------------------------------------------------------
# Commands may need to know the format version.
set(CMAKE_IMPORT_FILE_VERSION 1)
# Protect against multiple inclusion, which would fail when already imported targets are added once more.
set(_targetsDefined)
set(_targetsNotDefined)
set(_expectedTargets)
foreach(_expectedTarget HighFive::HighFive HighFive::Include)
list(APPEND _expectedTargets ${_expectedTarget})
if(NOT TARGET ${_expectedTarget})
list(APPEND _targetsNotDefined ${_expectedTarget})
endif()
if(TARGET ${_expectedTarget})
list(APPEND _targetsDefined ${_expectedTarget})
endif()
endforeach()
if("${_targetsDefined}" STREQUAL "${_expectedTargets}")
unset(_targetsDefined)
unset(_targetsNotDefined)
unset(_expectedTargets)
set(CMAKE_IMPORT_FILE_VERSION)
cmake_policy(POP)
return()
endif()
if(NOT "${_targetsDefined}" STREQUAL "")
message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n")
endif()
unset(_targetsDefined)
unset(_targetsNotDefined)
unset(_expectedTargets)
# Compute the installation prefix relative to this file.
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
if(_IMPORT_PREFIX STREQUAL "/")
set(_IMPORT_PREFIX "")
endif()
# Create imported target HighFive::HighFive
add_library(HighFive::HighFive INTERFACE IMPORTED)
set_target_properties(HighFive::HighFive PROPERTIES
INTERFACE_LINK_LIBRARIES "HighFive::Include;HDF5::HDF5"
)
# Create imported target HighFive::Include
add_library(HighFive::Include INTERFACE IMPORTED)
set_target_properties(HighFive::Include PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
if(CMAKE_VERSION VERSION_LESS 3.0.0)
message(FATAL_ERROR "This file relies on consumers using CMake 3.0.0 or greater.")
endif()
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/HighFiveTargets-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
# Cleanup temporary variables.
set(_IMPORT_PREFIX)
# Loop over all imported files and verify that they actually exist
foreach(target ${_IMPORT_CHECK_TARGETS} )
foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )
if(NOT EXISTS "${file}" )
message(FATAL_ERROR "The imported target \"${target}\" references the file
\"${file}\"
but this file does not exist. Possible reasons include:
* The file was deleted, renamed, or moved to another location.
* An install or uninstall procedure did not complete successfully.
* The installation package was faulty and contained
\"${CMAKE_CURRENT_LIST_FILE}\"
but not all the files it references.
")
endif()
endforeach()
unset(_IMPORT_CHECK_FILES_FOR_${target})
endforeach()
unset(_IMPORT_CHECK_TARGETS)
# This file does not depend on other imported targets which have
# been exported from the same project but in a separate export set.
# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)
cmake_policy(POP)

View File

@ -0,0 +1,189 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
doctest
-----
This module defines a function to help use the doctest test framework.
The :command:`doctest_discover_tests` discovers tests by asking the compiled test
executable to enumerate its tests. This does not require CMake to be re-run
when tests change. However, it may not work in a cross-compiling environment,
and setting test properties is less convenient.
This command is intended to replace use of :command:`add_test` to register
tests, and will create a separate CTest test for each doctest test case. Note
that this is in some cases less efficient, as common set-up and tear-down logic
cannot be shared by multiple test cases executing in the same instance.
However, it provides more fine-grained pass/fail information to CTest, which is
usually considered as more beneficial. By default, the CTest test name is the
same as the doctest name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
.. command:: doctest_discover_tests
Automatically add tests with CTest by querying the compiled test executable
for available tests::
doctest_discover_tests(target
[TEST_SPEC arg1...]
[EXTRA_ARGS arg1...]
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[ADD_LABELS value]
[TEST_LIST var]
[JUNIT_OUTPUT_DIR dir]
)
``doctest_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-test-cases`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
in order to function in a cross-compiling environment.
Additionally, setting properties on tests is somewhat less convenient, since
the tests are not available at CMake time. Additional test properties may be
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
more fine-grained test control is needed, custom content may be provided
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
The options are:
``target``
Specifies the doctest executable, which must be a known CMake executable
target. CMake will substitute the location of the built executable when
running the test.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the doctest executable with the ``--list-test-cases`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
``WORKING_DIRECTORY dir``
Specifies the directory in which to run the discovered test cases. If this
option is not provided, the current binary directory is used.
``TEST_PREFIX prefix``
Specifies a ``prefix`` to be prepended to the name of each discovered test
case. This can be useful when the same test executable is being used in
multiple calls to ``doctest_discover_tests()`` but with different
``TEST_SPEC`` or ``EXTRA_ARGS``.
``TEST_SUFFIX suffix``
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
``PROPERTIES name1 value1...``
Specifies additional properties to be set on all tests discovered by this
invocation of ``doctest_discover_tests``.
``ADD_LABELS value``
Specifies if the test labels should be set automatically.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
executable is being used in multiple calls to ``doctest_discover_tests()``.
Note that this variable is only available in CTest.
``JUNIT_OUTPUT_DIR dir``
If specified, the parameter is passed along with ``--reporters=junit``
and ``--out=`` to the test executable. The actual file name is the same
as the test target, including prefix and suffix. This should be used
instead of EXTRA_ARGS to avoid race conditions writing the XML result
output when using parallel test execution.
#]=======================================================================]
#------------------------------------------------------------------------------
function(doctest_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;JUNIT_OUTPUT_DIR"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;ADD_LABELS"
${ARGN}
)
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
get_property(crosscompiling_emulator
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_ADD_LABELS=${_ADD_LABELS}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "TEST_JUNIT_OUTPUT_DIR=${_JUNIT_OUTPUT_DIR}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_DOCTEST_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(NOT CMAKE_VERSION VERSION_LESS 3.10)
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
else()
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
if(NOT ${test_include_file_set})
set_property(DIRECTORY
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
endif()
endif()
endfunction()
###############################################################################
set(_DOCTEST_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/doctestAddTests.cmake
)

View File

@ -0,0 +1,120 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(add_labels ${TEST_ADD_LABELS})
set(junit_output_dir "${TEST_JUNIT_OUTPUT_DIR}")
set(script)
set(suite)
set(tests)
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
)
endif()
if("${spec}" MATCHES .)
set(spec "--test-case=${spec}")
endif()
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-cases
OUTPUT_VARIABLE output
RESULT_VARIABLE result
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
# Parse output
foreach(line ${output})
if("${line}" STREQUAL "===============================================================================" OR "${line}" MATCHES [==[^\[doctest\] ]==])
continue()
endif()
set(test ${line})
set(labels "")
if(${add_labels})
# get test suite that test belongs to
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" --test-case=${test} --list-test-suites
OUTPUT_VARIABLE labeloutput
RESULT_VARIABLE labelresult
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(NOT ${labelresult} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${labelresult}\n"
" Output: ${labeloutput}\n"
)
endif()
string(REPLACE "\n" ";" labeloutput "${labeloutput}")
foreach(labelline ${labeloutput})
if("${labelline}" STREQUAL "===============================================================================" OR "${labelline}" MATCHES [==[^\[doctest\] ]==])
continue()
endif()
list(APPEND labels ${labelline})
endforeach()
endif()
if(NOT "${junit_output_dir}" STREQUAL "")
# turn testname into a valid filename by replacing all special characters with "-"
string(REGEX REPLACE "[/\\:\"|<>]" "-" test_filename "${test}")
set(TEST_JUNIT_OUTPUT_PARAM "--reporters=junit" "--out=${junit_output_dir}/${prefix}${test_filename}${suffix}.xml")
else()
unset(TEST_JUNIT_OUTPUT_PARAM)
endif()
# use escape commas to handle properly test cases with commas inside the name
string(REPLACE "," "\\," test_name ${test})
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
"--test-case=${test_name}"
"${TEST_JUNIT_OUTPUT_PARAM}"
${extra_args}
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
LABELS ${labels}
)
unset(labels)
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

View File

@ -0,0 +1,6 @@
if(NOT TARGET doctest::doctest)
# Provide path for scripts
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
include("${CMAKE_CURRENT_LIST_DIR}/doctestTargets.cmake")
endif()

View File

@ -0,0 +1,70 @@
# This is a basic version file for the Config-mode of find_package().
# It is used by write_basic_package_version_file() as input file for configure_file()
# to create a version-file which can be installed along a config.cmake file.
#
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
# the requested version string are exactly the same and it sets
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version,
# but only if the requested major version is the same as the current one.
# The variable CVF_VERSION must be set before calling configure_file().
set(PACKAGE_VERSION "2.4.11")
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
if("2.4.11" MATCHES "^([0-9]+)\\.")
set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
endif()
else()
set(CVF_VERSION_MAJOR "2.4.11")
endif()
if(PACKAGE_FIND_VERSION_RANGE)
# both endpoints of the range must have the expected major version
math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1")
if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR)
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT)))
set(PACKAGE_VERSION_COMPATIBLE FALSE)
elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX)
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX)))
set(PACKAGE_VERSION_COMPATIBLE TRUE)
else()
set(PACKAGE_VERSION_COMPATIBLE FALSE)
endif()
else()
if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR)
set(PACKAGE_VERSION_COMPATIBLE TRUE)
else()
set(PACKAGE_VERSION_COMPATIBLE FALSE)
endif()
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
endif()
# if the installed project requested no architecture check, don't perform the check
if("FALSE")
return()
endif()
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "" STREQUAL "")
return()
endif()
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "")
math(EXPR installedBits " * 8")
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()

View File

@ -0,0 +1,99 @@
# Generated by CMake
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6)
message(FATAL_ERROR "CMake >= 2.6.0 required")
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 2.6...3.21)
#----------------------------------------------------------------
# Generated CMake target import file.
#----------------------------------------------------------------
# Commands may need to know the format version.
set(CMAKE_IMPORT_FILE_VERSION 1)
# Protect against multiple inclusion, which would fail when already imported targets are added once more.
set(_targetsDefined)
set(_targetsNotDefined)
set(_expectedTargets)
foreach(_expectedTarget doctest::doctest)
list(APPEND _expectedTargets ${_expectedTarget})
if(NOT TARGET ${_expectedTarget})
list(APPEND _targetsNotDefined ${_expectedTarget})
endif()
if(TARGET ${_expectedTarget})
list(APPEND _targetsDefined ${_expectedTarget})
endif()
endforeach()
if("${_targetsDefined}" STREQUAL "${_expectedTargets}")
unset(_targetsDefined)
unset(_targetsNotDefined)
unset(_expectedTargets)
set(CMAKE_IMPORT_FILE_VERSION)
cmake_policy(POP)
return()
endif()
if(NOT "${_targetsDefined}" STREQUAL "")
message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n")
endif()
unset(_targetsDefined)
unset(_targetsNotDefined)
unset(_expectedTargets)
# Compute the installation prefix relative to this file.
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
if(_IMPORT_PREFIX STREQUAL "/")
set(_IMPORT_PREFIX "")
endif()
# Create imported target doctest::doctest
add_library(doctest::doctest INTERFACE IMPORTED)
set_target_properties(doctest::doctest PROPERTIES
INTERFACE_COMPILE_FEATURES "cxx_std_11"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
if(CMAKE_VERSION VERSION_LESS 3.0.0)
message(FATAL_ERROR "This file relies on consumers using CMake 3.0.0 or greater.")
endif()
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/doctestTargets-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
# Cleanup temporary variables.
set(_IMPORT_PREFIX)
# Loop over all imported files and verify that they actually exist
foreach(target ${_IMPORT_CHECK_TARGETS} )
foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )
if(NOT EXISTS "${file}" )
message(FATAL_ERROR "The imported target \"${target}\" references the file
\"${file}\"
but this file does not exist. Possible reasons include:
* The file was deleted, renamed, or moved to another location.
* An install or uninstall procedure did not complete successfully.
* The installation package was faulty and contained
\"${CMAKE_CURRENT_LIST_FILE}\"
but not all the files it references.
")
endif()
endforeach()
unset(_IMPORT_CHECK_FILES_FOR_${target})
endforeach()
unset(_IMPORT_CHECK_TARGETS)
# This file does not depend on other imported targets which have
# been exported from the same project but in a separate export set.
# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)
cmake_policy(POP)

Binary file not shown.

View File

@ -0,0 +1,7 @@
iterations <- 50
dt <- 100
list(
timesteps = rep(dt, iterations),
store_result = TRUE
)

Binary file not shown.

View File

@ -0,0 +1,4 @@
list(
timesteps = rep(50, 100),
store_result = TRUE
)

Binary file not shown.

View File

@ -0,0 +1,10 @@
iterations <- 500
dt <- 50
out_save <- seq(5, iterations, by = 5)
list(
timesteps = rep(dt, iterations),
store_result = TRUE,
out_save = out_save
)

Binary file not shown.

View File

@ -0,0 +1,10 @@
iterations <- 20000
dt <- 200
out_save <- seq(50, iterations, by = 50)
list(
timesteps = rep(dt, iterations),
store_result = TRUE,
out_save = out_save
)

Binary file not shown.

View File

@ -0,0 +1,11 @@
iterations <- 200
dt <- 1000
out_save <- c(1, 2, seq(5, iterations, by=5))
## out_save <- seq(1, iterations)
list(
timesteps = rep(dt, iterations),
store_result = TRUE,
out_save = out_save
)

View File

@ -22,6 +22,7 @@ add_library(POETLib
Init/DiffusionInit.cpp
Init/ChemistryInit.cpp
IO/checkpoint.cpp
IO/StatsIO.cpp
DataStructures/Field.cpp
Transport/DiffusionModule.cpp
Chemistry/ChemistryModule.cpp

Some files were not shown because too many files have changed in this diff Show More