Program Listing for File h5ppFile.h

Return to documentation for file (/home/david/GitProjects/h5pp/h5pp/include/h5pp/details/h5ppFile.h)

#pragma once

#include "h5ppConstants.h"
#include "h5ppDimensionType.h"
#include "h5ppEigen.h"
#include "h5ppEnums.h"
#include "h5ppFilesystem.h"
#include "h5ppHdf5.h"
#include "h5ppHid.h"
#include "h5ppLogger.h"
#include "h5ppOptional.h"
#include "h5ppPropertyLists.h"
#include "h5ppScan.h"
#include "h5ppTypeCompoundCreate.h"
#include "h5ppUtils.h"
#include <hdf5.h>
#include <hdf5_hl.h>
#include <string>
#include <utility>

namespace h5pp {

    class File {
        private:
        fs::path             filePath;
        h5pp::FilePermission permission   = h5pp::FilePermission::RENAME;
        size_t               logLevel     = 2;
        bool                 logTimestamp = false;
        hid::h5e             error_stack;
        unsigned int         currentCompressionLevel = 0;
        void                 init() {
            h5pp::logger::setLogger("h5pp|init", logLevel, logTimestamp);
            h5pp::logger::log->debug("Initializing HDF5 file: [{}]", filePath.string());
            /* Set default error print output */
            error_stack                          = H5Eget_current_stack();
            herr_t turnOffAutomaticErrorPrinting = H5Eset_auto2(error_stack, nullptr, nullptr);
            if(turnOffAutomaticErrorPrinting < 0) {
                H5Eprint(H5E_DEFAULT, stderr);
                throw std::runtime_error("Failed to turn off H5E error printing");
            }
            // The following function can modify the resulting filePath depending on permission.
            filePath = h5pp::hdf5::createFile(filePath, permission, plists);
            h5pp::type::compound::initTypes();
            h5pp::logger::setLogger("h5pp|" + filePath.filename().string(), logLevel, logTimestamp);
            h5pp::logger::log->debug("Successfully initialized file [{}]", filePath.string());
        }

        public:
        // The following struct contains modifiable property lists.
        // This allows us to use h5pp with MPI, for instance.
        // Unmodified, these default to serial (non-MPI) use.
        // We expose these property lists here so the user may set them as needed.
        // To enable MPI, the user can call H5Pset_fapl_mpio(hid_t file_access_plist, MPI_Comm comm, MPI_Info info)
        // The user is responsible for linking to MPI and learning how to set properties for MPI usage
        PropertyLists plists;

        File() {
            h5pp::logger::setLogger("h5pp", logLevel, logTimestamp);
            h5pp::logger::log->debug("Default-constructing h5pp file object");
        }

        File(const File &other) {
            h5pp::logger::log->debug("Copy-constructing this file [{}] from given file: [{}]", filePath.string(), other.getFilePath());
            *this = other;
        }

        explicit File(const std::string &filePath_, h5pp::FilePermission permission_ = h5pp::FilePermission::RENAME, size_t logLevel_ = 2, bool logTimestamp_ = false)
            : filePath(filePath_), permission(permission_), logLevel(logLevel_), logTimestamp(logTimestamp_) {
            init();
        }

        explicit File(const std::string &filePath_, unsigned int H5F_ACC_FLAGS, size_t logLevel_ = 2, bool logTimestamp_ = false)
            : filePath(filePath_), logLevel(logLevel_), logTimestamp(logTimestamp_) {
            permission = h5pp::hdf5::convertFileAccessFlags(H5F_ACC_FLAGS);
            init();
        }

        ~File() {
            h5pp::logger::log->debug("Closing file [{}]", filePath.string());
            H5garbage_collect();
            H5Eprint(H5E_DEFAULT, stderr);
        }

        File &operator=(const File &other) {
            h5pp::logger::log->debug("Assignment to this file [{}] from given file: [{}]", filePath.string(), other.getFilePath());
            if(&other != this) {
                logLevel     = other.logLevel;
                logTimestamp = other.logTimestamp;
                permission   = other.permission;
                filePath     = other.filePath;
                plists       = other.plists;
                h5pp::logger::setLogger("h5pp|" + filePath.filename().string(), logLevel, logTimestamp);
            }
            return *this;
        }

        void flush() {
            h5pp::logger::log->trace("Flushing caches");
            H5Fflush(openFileHandle(), H5F_scope_t::H5F_SCOPE_GLOBAL);
            H5garbage_collect();
            H5Eprint(H5E_DEFAULT, stderr);
        }

        [[nodiscard]] hid::h5f openFileHandle() const {
            h5pp::logger::setLogger("h5pp|" + filePath.filename().string(), logLevel, logTimestamp);
            if(permission == h5pp::FilePermission::READONLY) {
                h5pp::logger::log->trace("Opening file in READONLY mode");
                hid_t fileHandle = H5Fopen(filePath.string().c_str(), H5F_ACC_RDONLY, plists.fileAccess);
                if(fileHandle < 0) {
                    H5Eprint(H5E_DEFAULT, stderr);
                    throw std::runtime_error(h5pp::format("Failed to open file in read-only mode [{}]", filePath.string()));
                } else
                    return fileHandle;
            } else {
                h5pp::logger::log->trace("Opening file in READWRITE mode");
                hid_t fileHandle = H5Fopen(filePath.string().c_str(), H5F_ACC_RDWR, plists.fileAccess);
                if(fileHandle < 0) {
                    H5Eprint(H5E_DEFAULT, stderr);
                    throw std::runtime_error(h5pp::format("Failed to open file in read-write mode [{}]", filePath.string()));
                } else
                    return fileHandle;
            }
        }

        /*
         *
         * Functions for file properties
         *
         */
        [[nodiscard]] h5pp::FilePermission getFilePermission() const { return permission; }
        [[nodiscard]] std::string          getFileName() const { return filePath.filename().string(); }
        [[nodiscard]] std::string          getFilePath() const { return filePath.string(); }
        void                               setFilePermission(h5pp::FilePermission permission_) { permission = permission_; }

        void setDriver_sec2() {
            plists.fileAccess = H5Fget_access_plist(openFileHandle());
            H5Pset_fapl_sec2(plists.fileAccess);
        }
        void setDriver_stdio() {
            plists.fileAccess = H5Fget_access_plist(openFileHandle());
            H5Pset_fapl_stdio(plists.fileAccess);
        }
        void setDriver_core(bool writeOnClose = true, size_t bytesPerMalloc = 10240000) {
            plists.fileAccess = H5Fget_access_plist(openFileHandle());
            H5Pset_fapl_core(plists.fileAccess, bytesPerMalloc, static_cast<hbool_t>(writeOnClose));
        }
#ifdef H5_HAVE_PARALLEL
        void setDriver_mpio(MPI_Comm comm, MPI_Info info) {
            plists.fileAccess = H5Fget_access_plist(openFileHandle());
            H5Pset_fapl_mpio(plists.fileAccess, comm, info);
        }
#endif

        /*
         *
         * Functions for managing file location
         *
         */

        [[maybe_unused]] fs::path copyFileTo(const std::string &targetFilePath, const FilePermission &perm = FilePermission::COLLISION_FAIL) const {
            return h5pp::hdf5::copyFile(getFilePath(), targetFilePath, perm, plists);
        }

        [[maybe_unused]] fs::path moveFileTo(const std::string &targetFilePath, const FilePermission &perm = FilePermission::COLLISION_FAIL) {
            auto newPath = h5pp::hdf5::moveFile(getFilePath(), targetFilePath, perm, plists);
            if(fs::exists(newPath)) { filePath = newPath; }
            return newPath;
        }

        /*
         *
         * Functions for transfering contents between locations or files
         *
         */

        void copyLinkToFile(const std::string &   localLinkPath,
                            const std::string &   targetFilePath,
                            const std::string &   targetLinkPath,
                            const FilePermission &targetFileCreatePermission = FilePermission::READWRITE) const {
            return h5pp::hdf5::copyLink(getFilePath(), localLinkPath, targetFilePath, targetLinkPath, targetFileCreatePermission, plists);
        }

        void copyLinkFromFile(const std::string &localLinkPath, const std::string &sourceFilePath, const std::string &sourceLinkPath) {
            return h5pp::hdf5::copyLink(sourceFilePath, sourceLinkPath, getFilePath(), localLinkPath, h5pp::FilePermission::READWRITE, plists);
        }

        template<typename h5x_tgt, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x_tgt>>
        void copyLinkToLocation(const std::string &localLinkPath, const h5x_tgt &targetLocationId, const std::string &targetLinkPath) const {
            return h5pp::hdf5::copyLink(openFileHandle(), localLinkPath, targetLocationId, targetLinkPath, plists);
        }

        template<typename h5x_src, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x_src>>
        void copyLinkFromLocation(const std::string &localLinkPath, const h5x_src &sourceLocationId, const std::string &sourceLinkPath) {
            return h5pp::hdf5::copyLink(sourceLocationId, sourceLinkPath, openFileHandle(), localLinkPath, h5pp::FilePermission::READWRITE, plists);
        }

        /*
         *
         * Functions for logging
         *
         */
        [[nodiscard]] size_t getLogLevel() const { return logLevel; }
        void                 setLogLevel(size_t logLevelZeroToFive) {
            logLevel = logLevelZeroToFive;
            h5pp::logger::setLogLevel(logLevelZeroToFive);
        }

        /*
         *
         * Functions related to datasets
         *
         */

        void setCompressionLevel(unsigned int compressionLevelZeroToNine) { currentCompressionLevel = h5pp::hdf5::getValidCompressionLevel(compressionLevelZeroToNine); }
        [[nodiscard]] unsigned int getCompressionLevel() const { return currentCompressionLevel; }
        [[nodiscard]] unsigned int getCompressionLevel(std::optional<unsigned int> desiredCompressionLevel) const {
            if(desiredCompressionLevel)
                return h5pp::hdf5::getValidCompressionLevel(desiredCompressionLevel.value());
            else
                return currentCompressionLevel;
        }

        void createGroup(std::string_view group_relative_name) { h5pp::hdf5::createGroup(openFileHandle(), group_relative_name, std::nullopt, plists); }

        void resizeDataset(DsetInfo &info, const DimsType &newDimensions, std::optional<h5pp::ResizeMode> mode_override = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to resize dataset on read-only file [{}]", filePath.string()));
            h5pp::hdf5::resizeDataset(info, newDimensions, mode_override);
        }

        template<typename DsetDimsType = std::initializer_list<hsize_t>, typename = h5pp::type::sfinae::enable_if_is_integral_iterable_or_num<DsetDimsType>>
        DsetInfo resizeDataset(std::string_view dsetPath, const DsetDimsType &newDimensions, std::optional<h5pp::ResizeMode> mode = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to resize dataset on read-only file [{}]", filePath.string()));
            Options options;
            options.linkPath   = dsetPath;
            options.resizeMode = mode;
            auto info          = h5pp::scan::getDsetInfo(openFileHandle(), dsetPath, options, plists);
            if(not info.dsetExists.value()) throw std::runtime_error(h5pp::format("Failed to resize dataset [{}]: dataset does not exist", dsetPath));
            h5pp::hdf5::resizeDataset(info, h5pp::util::getDimVector(newDimensions), mode);
            return info;
        }

        void createDataset(DsetInfo &info) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to create dataset on read-only file [{}]", filePath.string()));
            h5pp::hdf5::createDataset(info, plists);
        }

        DsetInfo createDataset(const Options &options) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to create dataset on read-only file [{}]", filePath.string()));
            options.assertWellDefined();
            if(not options.linkPath) throw std::runtime_error(h5pp::format("Error creating dataset: No dataset path specified"));
            if(not options.dataDims) throw std::runtime_error(h5pp::format("Error creating dataset [{}]: Dimensions or size not specified", options.linkPath.value()));
            if(not options.h5Type) throw std::runtime_error(h5pp::format("Error creating dataset [{}]: HDF5 type not specified", options.linkPath.value()));
            auto dsetInfo = h5pp::scan::getDsetInfo(openFileHandle(), options, plists);
            h5pp::File::createDataset(dsetInfo);
            return dsetInfo;
        }

        DsetInfo createDataset(std::optional<hid::h5t>     h5Type,
                               std::string_view            dsetPath,
                               const DimsType &            dsetDims,
                               std::optional<H5D_layout_t> h5Layout     = std::nullopt,
                               const OptDimsType &         dsetDimsChunk = std::nullopt,
                               const OptDimsType &         dsetDimsMax   = std::nullopt,
                               std::optional<unsigned int> compression   = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to create dataset on read-only file [{}]", filePath.string()));
            Options options;
            options.linkPath      = dsetPath;
            options.dataDims      = dsetDims;
            options.dsetDimsChunk = dsetDimsChunk;
            options.dsetDimsMax   = dsetDimsMax;
            options.h5Type        = std::move(h5Type);
            options.h5Layout      = h5Layout;
            options.compression   = getCompressionLevel(compression);
            return createDataset(options);
        }

        template<typename DataType>
        DsetInfo createDataset(const DataType &data, const Options &options) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to create dataset on read-only file [{}]", filePath.string()));
            auto dsetInfo = h5pp::scan::getDsetInfo(openFileHandle(), data, options, plists);
            h5pp::File::createDataset(dsetInfo);
            return dsetInfo;
        }

        template<typename DataType>
        DsetInfo createDataset(const DataType &            data,
                               std::string_view            dsetPath,
                               const OptDimsType &         dataDims      = std::nullopt,
                               std::optional<H5D_layout_t> h5Layout     = std::nullopt,
                               const OptDimsType &         dsetDimsChunk = std::nullopt,
                               const OptDimsType &         dsetDimsMax   = std::nullopt,
                               std::optional<unsigned int> compression   = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to create dataset on read-only file [{}]", filePath.string()));
            Options options;
            options.linkPath      = dsetPath;
            options.dataDims      = dataDims;
            options.dsetDimsChunk = dsetDimsChunk;
            options.dsetDimsMax   = dsetDimsMax;
            options.h5Type        = h5pp::util::getH5Type<DataType>();
            options.h5Layout      = h5Layout;
            options.compression   = getCompressionLevel(compression);
            // If dsetdims is a nullopt we can infer its dimensions from the given dataset
            return createDataset(data, options);
        }

        template<typename DataType>
        void writeDataset(const DataType &data, DsetInfo &dsetInfo, const Options &options = Options()) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            if(not dsetInfo.dsetExists or not dsetInfo.dsetExists.value()) createDataset(dsetInfo);
            auto dataInfo = h5pp::scan::getDataInfo(data,options);
            resizeDataset(dsetInfo, dataInfo.dataDims.value());
            h5pp::hdf5::writeDataset(data, dataInfo, dsetInfo, plists);
        }

        template<typename DataType>
        void writeDataset(const DataType &data, const DataInfo &dataInfo, DsetInfo &dsetInfo) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            // The infos passed should be parsed and ready to write with
            if(not dsetInfo.dsetExists or not dsetInfo.dsetExists.value()) createDataset(dsetInfo);
            resizeDataset(dsetInfo, dataInfo.dataDims.value());
            h5pp::hdf5::writeDataset(data, dataInfo, dsetInfo, plists);
        }

        template<typename DataType>
        DsetInfo writeDataset(const DataType &data, const Options &options) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            options.assertWellDefined();
            auto dataInfo = h5pp::scan::getDataInfo(data, options);
            auto dsetInfo = h5pp::scan::getDsetInfo(openFileHandle(), data, options, plists); // Creates if it doesn't exist, otherwise it just fills the meta data
            writeDataset(data, dataInfo, dsetInfo);
            return dsetInfo;
        }

        template<typename DataType>
        DsetInfo writeDataset(const DataType &            data,
                              std::string_view            dsetPath,
                              const OptDimsType &         dataDims  = std::nullopt,
                              std::optional<H5D_layout_t> h5Layout = std::nullopt,
                              const OptDimsType &         dsetDimsChunk = std::nullopt,
                              const OptDimsType &         dsetDimsMax   = std::nullopt,
                              std::optional<hid::h5t>     h5Type        = std::nullopt,
                              std::optional<ResizeMode>   resizeMode    = std::nullopt,
                              std::optional<unsigned int> compression = std::nullopt)
        {
            Options options;
            options.linkPath      = dsetPath;
            options.dataDims      = dataDims;
            options.dsetDimsChunk = dsetDimsChunk;
            options.dsetDimsMax   = dsetDimsMax;
            options.h5Layout      = h5Layout;
            options.h5Type        = std::move(h5Type);
            options.resizeMode    = resizeMode;
            options.compression   = getCompressionLevel(compression);
            return writeDataset(data, options);
        }

        template<typename DataType>
        DsetInfo writeDataset(const DataType &            data,
                              std::string_view            dsetPath,
                              hid::h5t &                  h5Type,
                              const OptDimsType &         dataDims  = std::nullopt,
                              std::optional<H5D_layout_t> h5Layout = std::nullopt,
                              const OptDimsType &         dsetDimsChunk = std::nullopt,
                              const OptDimsType &         dsetDimsMax   = std::nullopt,
                              std::optional<ResizeMode>   resizeMode    = std::nullopt,
                              std::optional<unsigned int> compression = std::nullopt)
        {
            Options options;
            options.linkPath      = dsetPath;
            options.dataDims      = dataDims;
            options.dsetDimsChunk = dsetDimsChunk;
            options.dsetDimsMax   = dsetDimsMax;
            options.h5Layout      = h5Layout;
            options.h5Type        = h5Type;
            options.resizeMode    = resizeMode;
            options.compression   = getCompressionLevel(compression);
            return writeDataset(data, options);
        }

        template<typename DataType>
        DsetInfo writeDataset(const DataType &            data,
                              std::string_view            dsetPath,
                              H5D_layout_t                h5Layout,
                              const OptDimsType &         dataDims      = std::nullopt,
                              const OptDimsType &         dsetDimsChunk = std::nullopt,
                              const OptDimsType &         dsetDimsMax   = std::nullopt,
                              std::optional<hid::h5t>     h5Type        = std::nullopt,
                              std::optional<ResizeMode>   resizeMode    = std::nullopt,
                              std::optional<unsigned int> compression = std::nullopt)
        {
            Options options;
            options.linkPath      = dsetPath;
            options.dataDims      = dataDims;
            options.dsetDimsChunk = dsetDimsChunk;
            options.dsetDimsMax   = dsetDimsMax;
            options.h5Layout      = h5Layout;
            options.h5Type        = std::move(h5Type);
            options.resizeMode    = resizeMode;
            options.compression   = getCompressionLevel(compression);
            return writeDataset(data, options);
        }

        template<typename DataType>
        DsetInfo writeDataset_compact(const DataType &data, std::string_view dsetPath, const OptDimsType &dataDims = std::nullopt, std::optional<hid::h5t> h5Type = std::nullopt) {
            Options options;
            options.linkPath    = dsetPath;
            options.dataDims    = dataDims;
            options.h5Layout    = H5D_COMPACT;
            options.h5Type      = std::move(h5Type);
            options.compression = 0;
            return writeDataset(data, options);
        }

        template<typename DataType>
        DsetInfo
            writeDataset_contiguous(const DataType &data, std::string_view dsetPath, const OptDimsType &dataDims = std::nullopt, std::optional<hid::h5t> h5Type = std::nullopt) {
            Options options; // Get optional iterable should have three different return states, nullopt, empty or nonempty, ´,
            options.linkPath    = dsetPath;
            options.dataDims    = dataDims;
            options.h5Layout    = H5D_CONTIGUOUS;
            options.h5Type      = std::move(h5Type);
            options.compression = 0;
            return writeDataset(data, options);
        }

        template<typename DataType>
        DsetInfo writeDataset_chunked(const DataType &            data,
                                      std::string_view            dsetPath,
                                      const OptDimsType &         dataDims      = std::nullopt,
                                      const OptDimsType &         dsetDimsChunk = std::nullopt,
                                      const OptDimsType &         dsetDimsMax   = std::nullopt,
                                      std::optional<hid::h5t>     h5Type        = std::nullopt,
                                      std::optional<unsigned int> compression   = std::nullopt) {
            Options options; // Get optional iterable should have three different return states, nullopt, empty or nonempty, ´,
            options.linkPath      = dsetPath;
            options.dataDims      = dataDims;
            options.dsetDimsChunk = dsetDimsChunk;
            options.dsetDimsMax   = dsetDimsMax;
            options.h5Layout      = H5D_CHUNKED;
            options.h5Type        = std::move(h5Type);
            options.compression   = getCompressionLevel(compression);
            return writeDataset(data, options);
        }

        void writeSymbolicLink(std::string_view src_path, std::string_view tgt_path) {
            hid::h5f file = openFileHandle();
            h5pp::hdf5::writeSymbolicLink(file, src_path, tgt_path, plists);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        void readDataset(DataType &data, const DataInfo &dataInfo, const DsetInfo &dsetInfo) const {
            h5pp::hdf5::readDataset(data, dataInfo, dsetInfo, plists);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        DataType readDataset(const DataInfo &dataInfo, const DsetInfo &dsetInfo) const {
            DataType data;
            readDataset(data, dataInfo, dsetInfo);
            return data;
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        void readDataset(DataType &data, const DsetInfo &dsetInfo, const Options &options = Options()) const {
            h5pp::hdf5::resizeData(data, dsetInfo);
            auto dataInfo = h5pp::scan::getDataInfo(data, options);
            readDataset(data, dataInfo, dsetInfo);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        DataType readDataset(DsetInfo &dsetInfo, const Options &options = Options()) const {
            DataType data;
            readDataset(data, dsetInfo, options);
            return data;
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        DataType readDataset(DsetInfo &dsetInfo, const DimsType &dataDims) const {
            DataType data;
            Options  options;
            options.dataDims = dataDims;
            readDataset(data, dsetInfo, options);
            return data;
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        void readDataset(DataType &data, const Options &options) const {
            options.assertWellDefined();
            auto dsetInfo = h5pp::scan::readDsetInfo(openFileHandle(), options, plists);
            if(dsetInfo.dsetExists and not dsetInfo.dsetExists.value())
                throw std::runtime_error(h5pp::format("Cannot read dataset [{}]: It does not exist", options.linkPath.value()));

            h5pp::hdf5::resizeData(data, dsetInfo);
            auto dataInfo = h5pp::scan::getDataInfo(data, options);
            h5pp::hdf5::readDataset(data, dataInfo, dsetInfo, plists);
        }

        template<typename DataType>
        void readDataset(DataType &data, std::string_view dsetPath, const OptDimsType &dataDims = std::nullopt) const {
            Options options;
            options.linkPath = dsetPath;
            options.dataDims = dataDims;
            readDataset(data, options);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        DataType readDataset(std::string_view datasetPath, const OptDimsType &dataDims = std::nullopt) const {
            DataType data;
            readDataset(data, datasetPath, dataDims);
            return data;
        }

        template<typename DataType>
        void appendToDataset(DataType &data, const DataInfo &dataInfo, DsetInfo &dsetInfo, size_t axis) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            h5pp::hdf5::extendDataset(dsetInfo, dataInfo, axis);
            h5pp::hdf5::writeDataset(data, dataInfo, dsetInfo, plists);
        }

        template<typename DataType>
        void appendToDataset(DataType &data, DsetInfo &dsetInfo, size_t axis, const OptDimsType &dataDims = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            Options options;
            options.dataDims = dataDims;
            auto dataInfo    = h5pp::scan::getDataInfo(data, options);
            appendToDataset(data, dataInfo, dsetInfo, axis);
        }

        template<typename DataType>
        DsetInfo appendToDataset(DataType &data, size_t axis, const Options &options = Options()) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            options.assertWellDefined();
            auto dataInfo = h5pp::scan::getDataInfo(data, options);
            auto dsetInfo = h5pp::scan::readDsetInfo(openFileHandle(), options, plists);
            appendToDataset(data, dataInfo, dsetInfo, axis);
            return dsetInfo;
        }

        template<typename DataType>
        DsetInfo appendToDataset(DataType &data, std::string_view dsetPath, size_t axis, const OptDimsType &dataDims = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            Options options;
            options.linkPath = dsetPath;
            options.dataDims = dataDims;
            return appendToDataset(data, axis, options);
        }

        /*
         *
         * Functions related to attributes
         *
         */

        void createAttribute(AttrInfo &attrInfo) { h5pp::hdf5::createAttribute(attrInfo); }

        template<typename DataType>
        AttrInfo createAttribute(const DataType &data, const Options &options) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to create attribute on read-only file [{}]", filePath.string()));
            auto attrInfo = h5pp::scan::getAttrInfo(openFileHandle(), data, options, plists);
            createAttribute(attrInfo);
            return attrInfo;
        }

        template<typename DataType>
        void createAttribute(const DataType &data, const DimsType &dataDims, std::string_view attrName, std::string_view linkPath) {
            Options options;
            options.linkPath = linkPath;
            options.attrName = attrName;
            options.dataDims = dataDims;
            createAttribute(data, options);
        }

        template<typename DataType>
        void writeAttribute(const DataType &data, const Options &options) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            options.assertWellDefined();
            auto dataInfo = h5pp::scan::getDataInfo(data, options);
            auto attrInfo = createAttribute(data, options);
            h5pp::hdf5::writeAttribute(data, dataInfo, attrInfo);
        }

        template<typename DataType>
        void writeAttribute(const DataType &        data,
                            std::string_view        attrName,
                            std::string_view        linkPath,
                            const OptDimsType &     dataDims = std::nullopt,
                            std::optional<hid::h5t> h5Type   = std::nullopt) {
            Options options;
            options.linkPath = linkPath;
            options.attrName = attrName;
            options.dataDims = dataDims;
            options.h5Type   = std::move(h5Type);
            writeAttribute(data, options);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        void readAttribute(DataType &data, const Options &options) const {
            options.assertWellDefined();
            auto attrInfo = h5pp::scan::readAttrInfo(openFileHandle(), options, plists);
            if(attrInfo.linkExists and not attrInfo.linkExists.value())
                throw std::runtime_error(h5pp::format("Could not read attribute [{}] in link [{}]: "
                                                      "Link does not exist",
                                                      attrInfo.attrName.value(),
                                                      attrInfo.linkPath.value()));

            if(attrInfo.attrExists and not attrInfo.attrExists.value())
                throw std::runtime_error(h5pp::format("Could not read attribute [{}] in link [{}]: "
                                                      "Attribute does not exist",
                                                      attrInfo.attrName.value(),
                                                      attrInfo.linkPath.value()));

            h5pp::hdf5::resizeData(data, attrInfo);
            auto dataInfo = h5pp::scan::getDataInfo(data, options);
            h5pp::hdf5::readAttribute(data, dataInfo, attrInfo);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        void readAttribute(DataType &data, std::string_view attrName, std::string_view linkPath, const OptDimsType &dataDims = std::nullopt) const {
            Options options;
            options.linkPath = linkPath;
            options.attrName = attrName;
            options.dataDims = dataDims;
            readAttribute(data, options);
        }

        template<typename DataType, typename = std::enable_if_t<not std::is_const_v<DataType>>>
        [[nodiscard]] DataType readAttribute(std::string_view attrName, std::string_view linkPath, const OptDimsType &dataDims = std::nullopt) const {
            DataType data;
            readAttribute(data, attrName, linkPath, dataDims);
            return data;
        }

        [[nodiscard]] inline std::vector<std::string> getAttributeNames(std::string_view linkPath) const {
            return h5pp::hdf5::getAttributeNames(openFileHandle(), linkPath, std::nullopt, plists.linkAccess);
        }

        /*
         *
         *
         * Functions related to tables
         *
         *
         */

        TableInfo createTable(const hid::h5t &                  h5EntryType,
                              std::string_view                  tableName,
                              std::string_view                  tableTitle,
                              const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                              const std::optional<unsigned int> desiredCompressionLevel = std::nullopt

        ) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            auto tableInfo = h5pp::scan::newTableInfo(h5EntryType, tableName, tableTitle, desiredChunkSize, desiredCompressionLevel);
            h5pp::hdf5::createTable(openFileHandle(), tableInfo, plists);
            return tableInfo;
        }

        template<typename DataType>
        TableInfo appendTableEntries(const DataType &data, std::string_view tableName) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            auto info = h5pp::scan::getTableInfo(openFileHandle(), tableName, std::nullopt, plists);
            if(not info.tableExists.value()) throw std::runtime_error(h5pp::format("Cannot append to table [{}]: it does not exist", tableName));
            h5pp::hdf5::appendTableEntries(data, info);
            return info;
        }

        void addTableEntriesFrom(const h5pp::TableInfo &           srcInfo,
                                 h5pp::TableInfo &                 tgtInfo,
                                 TableSelection                    tableSelection,
                                 const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                 const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            if(not srcInfo.tableExists) throw std::runtime_error("Source table info has not been initialized");
            if(not srcInfo.tableExists.value()) throw std::runtime_error("Source table does not exist");
            if(not tgtInfo.tableExists or not tgtInfo.tableExists.value())
                tgtInfo = createTable(srcInfo.tableType.value(), tgtInfo.tablePath.value(), srcInfo.tableTitle.value(), desiredChunkSize, desiredCompressionLevel);
            hsize_t srcStartEntry = 0;
            hsize_t tgtStartEntry = 0;
            hsize_t numEntries    = 0;
            switch(tableSelection) {
                case h5pp::TableSelection::ALL: numEntries = srcInfo.numRecords.value(); break;
                case h5pp::TableSelection::FIRST:
                    numEntries = 1;
                    if(tgtInfo.numRecords.value() > 0) tgtStartEntry = tgtInfo.numRecords.value() - 1;
                    break;
                case h5pp::TableSelection::LAST:
                    numEntries = 1;
                    if(tgtInfo.numRecords.value() > 0) tgtStartEntry = tgtInfo.numRecords.value() - 1;
                    if(srcInfo.numRecords.value() > 0) srcStartEntry = srcInfo.numRecords.value() - 1;
                    break;
            }
            h5pp::hdf5::addTableEntriesFrom(srcInfo, tgtInfo, srcStartEntry, tgtStartEntry, numEntries);
        }

        TableInfo addTableEntriesFrom(const h5pp::TableInfo &           srcInfo,
                                      std::string_view                  tgtTableName,
                                      TableSelection                    tableSelection,
                                      const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                      const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
            auto tgtInfo = h5pp::scan::getTableInfo(openFileHandle(), tgtTableName, std::nullopt, plists);
            addTableEntriesFrom(srcInfo, tgtInfo, tableSelection, desiredChunkSize, desiredCompressionLevel);
            return tgtInfo;
        }

        template<typename h5x_src, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x_src>>
        TableInfo addTableEntriesFrom(const h5x_src &                   srcLocation,
                                      std::string_view                  srcTableName,
                                      std::string_view                  tgtTableName,
                                      TableSelection                    tableSelection,
                                      const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                      const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
            auto srcInfo = h5pp::scan::getTableInfo(srcLocation, srcTableName, std::nullopt, plists);
            return addTableEntriesFrom(srcInfo, tgtTableName, tableSelection, desiredChunkSize, desiredCompressionLevel);
        }

        void addTableEntriesFrom(const h5pp::TableInfo &           srcInfo,
                                 h5pp::TableInfo &                 tgtInfo,
                                 hsize_t                           srcStartEntry,
                                 hsize_t                           tgtStartEntry,
                                 hsize_t                           numEntries,
                                 const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                 const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
            if(permission == h5pp::FilePermission::READONLY) throw std::runtime_error(h5pp::format("Attempted to write on read-only file [{}]", filePath.string()));
            if(not srcInfo.tableExists) throw std::runtime_error("Source table info has not been initialized");
            if(not srcInfo.tableExists.value()) throw std::runtime_error("Source table does not exist");
            if(not tgtInfo.tableExists.value())
                tgtInfo = createTable(srcInfo.tableType.value(), tgtInfo.tablePath.value(), srcInfo.tableTitle.value(), desiredChunkSize, desiredCompressionLevel);
            h5pp::hdf5::addTableEntriesFrom(srcInfo, tgtInfo, srcStartEntry, tgtStartEntry, numEntries);
        }

        template<typename h5x_src, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x_src>>
        TableInfo addTableEntriesFrom(const h5pp::TableInfo &           srcInfo,
                                      std::string_view                  tgtTableName,
                                      hsize_t                           srcStartEntry,
                                      hsize_t                           tgtStartEntry,
                                      hsize_t                           numEntries,
                                      const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                      const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
            auto tgtInfo = h5pp::scan::getTableInfo(openFileHandle(), tgtTableName, std::nullopt, plists);
            addTableEntriesFrom(srcInfo, tgtInfo, srcStartEntry, tgtStartEntry, numEntries, desiredChunkSize, desiredCompressionLevel);
            return tgtInfo;
        }

        template<typename h5x_src, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x_src>>
        TableInfo addTableEntriesFrom(const h5x_src &                   srcLocation,
                                      std::string_view                  srcTableName,
                                      std::string_view                  tgtTableName,
                                      hsize_t                           srcStartIdx,
                                      hsize_t                           tgtStartIdx,
                                      hsize_t                           numRecords,
                                      const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                      const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
            auto srcInfo = h5pp::scan::getTableInfo(srcLocation, srcTableName, std::nullopt, plists);
            return addTableEntriesFrom(srcInfo, tgtTableName, srcStartIdx, tgtStartIdx, numRecords, desiredChunkSize, desiredCompressionLevel);
        }

        template<typename DataType>
        void readTableEntries(DataType &data, std::string_view tableName, std::optional<size_t> startEntry = std::nullopt, std::optional<size_t> numEntries = std::nullopt) const {
            auto info = h5pp::scan::getTableInfo(openFileHandle(), tableName, std::nullopt, plists);
            h5pp::hdf5::readTableEntries(data, info, startEntry, numEntries);
        }

        template<typename DataType>
        DataType readTableEntries(std::string_view tablePath, std::optional<size_t> startEntry = std::nullopt, std::optional<size_t> numEntries = std::nullopt) const {
            DataType data;
            readTableEntries(data, tablePath, startEntry, numEntries);
            return data;
        }

        template<typename DataType>
        void readTableField(DataType &            data,
                            std::string_view      tablePath,
                            std::string_view      fieldNames,
                            std::optional<size_t> startEntry = std::nullopt,
                            std::optional<size_t> numEntries = std::nullopt) const {
            auto info = h5pp::scan::getTableInfo(openFileHandle(), tablePath, std::nullopt, plists);
            h5pp::hdf5::readTableField(data, info, fieldNames, startEntry, numEntries);
        }

        template<typename DataType>
        DataType readTableField(std::string_view      tablePath,
                                std::string_view      fieldName,
                                std::optional<size_t> startEntry = std::nullopt,
                                std::optional<size_t> numEntries = std::nullopt) const {
            DataType data;
            readTableField(data, tablePath, fieldName, startEntry, numEntries);
            return data;
        }

        template<typename DataType>
        void readTableField(DataType &data, std::string_view tablePath, std::string_view fieldNames, TableSelection tableSelection) const {
            auto    info       = h5pp::scan::getTableInfo(openFileHandle(), tablePath, std::nullopt, plists);
            hsize_t startEntry = 0;
            hsize_t numEntries = 0;
            switch(tableSelection) {
                case h5pp::TableSelection::ALL:
                    startEntry = 0;
                    numEntries = info.numRecords.value();
                    break;
                case h5pp::TableSelection::FIRST:
                    startEntry = 0;
                    numEntries = 1;
                    break;
                case h5pp::TableSelection::LAST:
                    startEntry = info.numRecords.value() - 1;
                    numEntries = 1;
                    break;
            }
            h5pp::hdf5::readTableField(data, info, fieldNames, startEntry, numEntries);
        }

        template<typename DataType>
        DataType readTableField(std::string_view tablePath, std::string_view fieldName, TableSelection tableSelection) const {
            DataType data;
            readTableField(data, tablePath, fieldName, tableSelection);
            return data;
        }

        /*
         *
         *
         * Functions for querying
         *
         *
         */

        [[nodiscard]] int getDatasetRank(std::string_view datasetPath) const {
            auto dataset = h5pp::hdf5::openLink<hid::h5d>(openFileHandle(), datasetPath);
            return h5pp::hdf5::getRank(dataset);
        }

        [[nodiscard]] std::vector<hsize_t> getDatasetDimensions(std::string_view datasetPath) const {
            auto dataset = h5pp::hdf5::openLink<hid::h5d>(openFileHandle(), datasetPath);
            return h5pp::hdf5::getDimensions(dataset);
        }
        [[nodiscard]] std::optional<std::vector<hsize_t>> getDatasetMaxDimensions(std::string_view datasetPath) const {
            auto dataset = h5pp::hdf5::openLink<hid::h5d>(openFileHandle(), datasetPath);
            return h5pp::hdf5::getMaxDimensions(dataset);
        }

        [[nodiscard]] std::optional<std::vector<hsize_t>> getDatasetChunkDimensions(std::string_view datasetPath) const {
            auto dataset = h5pp::hdf5::openLink<hid::h5d>(openFileHandle(), datasetPath);
            return h5pp::hdf5::getChunkDimensions(dataset);
        }

        [[nodiscard]] bool linkExists(std::string_view link) const { return h5pp::hdf5::checkIfLinkExists(openFileHandle(), link, std::nullopt, plists.linkAccess); }

//        [[nodiscard]] std::vector<std::string> getLinks(std::string_view root = "/", long maxDepth = 0) const {
//            return h5pp::hdf5::getContentsOfLink<H5O_type_t::H5O_TYPE_UNKNOWN>(openFileHandle(), root, maxDepth, plists.linkAccess);
//        }
//
//        [[nodiscard]] std::vector<std::string> getDatasets(std::string_view root = "/", long maxDepth = 0) const {
//            return h5pp::hdf5::getContentsOfLink<H5O_type_t::H5O_TYPE_DATASET>(openFileHandle(), root, maxDepth, plists.linkAccess);
//        }
//
//        [[nodiscard]] std::vector<std::string> getGroups(std::string_view root = "/", long maxDepth = 0) const {
//            return h5pp::hdf5::getContentsOfLink<H5O_type_t::H5O_TYPE_GROUP>(openFileHandle(), root, maxDepth, plists.linkAccess);
//        }

        [[nodiscard]] std::vector<std::string> findLinks(std::string_view searchKey = "", std::string_view searchRoot = "/", long maxHits = -1, long maxDepth = -1) const {
            return h5pp::hdf5::findLinks<H5O_TYPE_UNKNOWN>(openFileHandle(), searchKey, searchRoot, maxHits, maxDepth, plists.linkAccess);
        }

        [[nodiscard]] std::vector<std::string> findDatasets(std::string_view searchKey = "", std::string_view searchRoot = "/", long maxHits = -1, long maxDepth = -1) const {
            return h5pp::hdf5::findLinks<H5O_TYPE_DATASET>(openFileHandle(), searchKey, searchRoot, maxHits, maxDepth, plists.linkAccess);
        }

        [[nodiscard]] std::vector<std::string> findGroups(std::string_view searchKey = "", std::string_view searchRoot = "/", long maxHits = -1, long maxDepth = -1) const {
            return h5pp::hdf5::findLinks<H5O_TYPE_GROUP>(openFileHandle(), searchKey, searchRoot, maxHits, maxDepth, plists.linkAccess);
        }

        [[nodiscard]] DsetInfo getDatasetInfo(std::string_view dsetPath) const {
            Options options;
            options.linkPath = h5pp::util::safe_str(dsetPath);
            return h5pp::scan::readDsetInfo(openFileHandle(), options, plists);
        }

        [[nodiscard]] AttrInfo getAttributeInfo(std::string_view linkPath, std::string_view attrName) const {
            Options options;
            options.linkPath = h5pp::util::safe_str(linkPath);
            options.attrName = h5pp::util::safe_str(attrName);
            return h5pp::scan::readAttrInfo(openFileHandle(), options, plists);
        }

        [[nodiscard]] TableInfo getTableInfo(std::string_view tablePath) const { return h5pp::scan::getTableInfo(openFileHandle(), tablePath, std::nullopt, plists); }

        [[nodiscard]] TypeInfo getTypeInfoDataset(std::string_view dsetPath) const { return h5pp::hdf5::getTypeInfo(openFileHandle(), dsetPath, std::nullopt, plists.linkAccess); }

        [[nodiscard]] TypeInfo getTypeInfoAttribute(std::string_view linkPath, std::string_view attrName) const {
            return h5pp::hdf5::getTypeInfo(openFileHandle(), linkPath, attrName, std::nullopt, std::nullopt, plists.linkAccess);
        }

        [[nodiscard]] std::vector<TypeInfo> getTypeInfoAttributes(std::string_view linkPath) const {
            return h5pp::hdf5::getTypeInfo_allAttributes(openFileHandle(), linkPath, std::nullopt, plists.linkAccess);
        }

        [[nodiscard]] bool fileIsValid() const { return h5pp::hdf5::fileIsValid(filePath); }
    };
}