Program Listing for File h5ppScan.h

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

#include "h5ppConstants.h"
#include "h5ppHdf5.h"
#include "h5ppInfo.h"
#include "h5ppTypeSfinae.h"
#include "h5ppUtils.h"

namespace h5pp::scan {

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline void fillDsetInfo(h5pp::DsetInfo &info, const h5x &loc, const Options &options, const PropertyLists &plists = PropertyLists()) {
        if(not options.linkPath) throw std::runtime_error("Could not fill dataset info: No dataset path was given in options");
        h5pp::logger::log->debug("Scanning metadata of dataset [{}]", options.linkPath.value());
        // Copy the location
        if constexpr(std::is_same_v<h5x, hid::h5f>) info.h5File = loc;
        if constexpr(std::is_same_v<h5x, hid::h5g>) info.h5Group = loc;
        if constexpr(std::is_same_v<h5x, hid::h5o>) {
            H5I_type_t type = H5Iget_type(loc);
            if(type == H5I_type_t::H5I_GROUP or type == H5I_type_t::H5I_FILE)
                info.h5ObjLoc = loc;
            else
                throw std::runtime_error("Given object type for location is not a group or a file");
        }
        if(not info.dsetPath) info.dsetPath = h5pp::util::safe_str(options.linkPath.value());
        if(not info.dsetExists) info.dsetExists = h5pp::hdf5::checkIfDatasetExists(info.getLocId(), info.dsetPath.value(), std::nullopt, plists.linkAccess);
        if(not info.dsetSlab) info.dsetSlab = options.dsetSlab;
        // If the dataset does not exist, there isn't much else to do so we return;
        if(info.dsetExists and not info.dsetExists.value()) return;
        // From here on the dataset exists
        if(not info.h5Dset) info.h5Dset = h5pp::hdf5::openLink<hid::h5d>(loc, options.linkPath.value(), info.dsetExists, plists.linkAccess);
        if(not info.h5Type) info.h5Type = H5Dget_type(info.h5Dset.value());
        if(not info.h5Space) info.h5Space = H5Dget_space(info.h5Dset.value());

        // Get the properties of the selected space
        if(not info.dsetByte) info.dsetByte = h5pp::hdf5::getBytesTotal(info.h5Dset.value(), info.h5Space, info.h5Type);
        if(not info.dsetSize) info.dsetSize = h5pp::hdf5::getSize(info.h5Space.value());
        if(not info.dsetRank) info.dsetRank = h5pp::hdf5::getRank(info.h5Space.value());
        if(not info.dsetDims) info.dsetDims = h5pp::hdf5::getDimensions(info.h5Space.value());

        // We read the layout from file. Note that it is not possible to change the layout on existing datasets! Read more here
        // https://support.hdfgroup.org/HDF5/Tutor/layout.html
        if(not info.h5PlistDsetCreate) info.h5PlistDsetCreate = H5Dget_create_plist(info.h5Dset.value());
        if(not info.h5PlistDsetAccess) info.h5PlistDsetAccess = H5Dget_access_plist(info.h5Dset.value());
        if(not info.h5Layout) info.h5Layout = H5Pget_layout(info.h5PlistDsetCreate.value());
        if(not info.dsetChunk) info.dsetChunk = h5pp::hdf5::getChunkDimensions(info.h5PlistDsetCreate.value());
        if(not info.dsetDimsMax) info.dsetDimsMax = h5pp::hdf5::getMaxDimensions(info.h5Space.value(), info.h5Layout.value());

        if(not info.resizeMode) info.resizeMode = options.resizeMode;
        if(not info.resizeMode) {
            if(info.h5Layout != H5D_CHUNKED)
                info.resizeMode = h5pp::ResizeMode::DO_NOT_RESIZE;
            else
                info.resizeMode = h5pp::ResizeMode::RESIZE_TO_FIT;
        }

        // Get c++ properties
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType(info.h5Type.value());

        h5pp::logger::log->trace("Scanned metadata {}", info.string());
        auto error_msg = h5pp::debug::reportCompatibility(info.h5Layout, info.dsetDims, info.dsetChunk, info.dsetDimsMax);
        if(not error_msg.empty()) throw std::runtime_error(h5pp::format("Scanned dataset metadata is not well defined: \n{}", error_msg));
    }

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline h5pp::DsetInfo readDsetInfo(const h5x &loc, const Options &options, const PropertyLists &plists = PropertyLists()) {
        if(not options.linkPath) throw std::runtime_error("Could not read dataset info: No dataset path was given in options");
        h5pp::DsetInfo info;
        fillDsetInfo(info, loc, options, plists);
        return info;
    }

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline h5pp::DsetInfo getDsetInfo(const h5x &loc, const Options &options, const PropertyLists &plists = PropertyLists()) {
        auto info = readDsetInfo(loc, options, plists);
        if(info.dsetExists.value()) return info;
        h5pp::logger::log->debug("Creating metadata for new dataset [{}]", options.linkPath.value());
        // First copy the parameters given in options
        info.dsetDims    = options.dataDims;
        info.dsetDimsMax = options.dsetDimsMax;
        info.dsetChunk   = options.dsetDimsChunk;
        info.dsetSlab    = options.dsetSlab;
        info.h5Type      = options.h5Type;
        info.h5Layout    = options.h5Layout;
        info.compression = options.compression;
        info.resizeMode  = options.resizeMode;

        // Some sanity checks
        if(not info.dsetDims)
            throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                  "Dimensions for new dataset must be specified when no data is given",
                                                  info.dsetPath.value()));
        if(not info.h5Type)
            throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                  "The HDF5 type for a new dataset must be specified when no data is given",
                                                  info.dsetPath.value()));

        if(info.dsetChunk) {
            // If dsetDimsChunk has been given then the layout is supposed to be chunked
            if(not info.h5Layout) info.h5Layout = H5D_CHUNKED;

            // Check that chunking options are sane
            if(info.dsetDims and info.dsetDims->size() != info.dsetChunk->size())
                throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                      "Dataset and chunk dimensions must be the same size: "
                                                      "dset dims {} | chunk dims {}",
                                                      info.dsetPath.value(),
                                                      info.dsetDims.value(),
                                                      info.dsetChunk.value()));

            if(info.h5Layout != H5D_CHUNKED)
                throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                      "Dataset chunk dimensions {} requires H5D_CHUNKED layout",
                                                      info.dsetPath.value(),
                                                      info.dsetChunk.value()));
        }

        // If dsetDimsMax has been given and any of them is H5S_UNLIMITED then the layout is supposed to be chunked
        if(info.dsetDimsMax) {
            // If dsetDimsMax has been given then the layout is supposed to be chunked
            if(not info.h5Layout) info.h5Layout = H5D_CHUNKED;
        }

        // Next infer the missing properties
        /* clang-format off */
        if(not info.dsetSize)    info.dsetSize      = h5pp::util::getSizeFromDimensions(info.dsetDims.value());
        if(not info.dsetRank)    info.dsetRank      = h5pp::util::getRankFromDimensions(info.dsetDims.value());
        if(not info.dsetByte)    info.dsetByte      = info.dsetSize.value() * h5pp::hdf5::getBytesPerElem(info.h5Type.value()); // Trick needed for strings.
        if(not info.h5Layout)    info.h5Layout      = h5pp::util::decideLayout(info.dsetByte.value());
        if(not info.dsetDimsMax) info.dsetDimsMax   = h5pp::util::decideDimensionsMax(info.dsetDims.value(), info.h5Layout.value());
        if(not info.dsetChunk)   info.dsetChunk     = h5pp::util::getChunkDimensions(h5pp::hdf5::getBytesPerElem(info.h5Type.value()), info.dsetDims.value(),info.dsetDimsMax,info.h5Layout);
        if(not info.compression) info.compression   = h5pp::hdf5::getValidCompressionLevel(info.compression);
        if(not info.resizeMode) {
            if(info.h5Layout != H5D_CHUNKED)
                info.resizeMode = h5pp::ResizeMode::DO_NOT_RESIZE;
            else
                info.resizeMode = h5pp::ResizeMode::RESIZE_TO_FIT;
        }
        /* clang-format on */

        info.h5PlistDsetCreate = H5Pcreate(H5P_DATASET_CREATE);
        info.h5PlistDsetAccess = H5Pcreate(H5P_DATASET_ACCESS);
        info.h5Space           = h5pp::util::getDsetSpace(info.dsetSize.value(), info.dsetDims.value(), info.h5Layout.value(), info.dsetDimsMax);
        h5pp::hdf5::setProperty_layout(info);    // Must go before setting chunk dims
        h5pp::hdf5::setProperty_chunkDims(info); // Will nullify chunkdims if not H5D_CHUNKED
        h5pp::hdf5::setProperty_compression(info);
        h5pp::hdf5::setSpaceExtent(info);

        // Get c++ properties
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType(info.h5Type.value());

        h5pp::logger::log->trace("Created metadata {}", info.string());
        auto error_msg = h5pp::debug::reportCompatibility(info.h5Layout, info.dsetDims, info.dsetChunk, info.dsetDimsMax);
        if(not error_msg.empty()) throw std::runtime_error(h5pp::format("Created dataset metadata is not well defined: \n{}", error_msg));
        return info;
    }

    template<typename DataType, typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline h5pp::DsetInfo getDsetInfo(const h5x &loc, const DataType &data, const Options &options = Options(), const PropertyLists &plists = PropertyLists()) {
        auto info = readDsetInfo(loc, options, plists);
        if(info.dsetExists.value()) return info;
        h5pp::logger::log->debug("Creating metadata for new dataset [{}]", options.linkPath.value());

        // First copy the parameters given in options
        info.dsetDims    = options.dataDims;
        info.dsetDimsMax = options.dsetDimsMax;
        info.dsetChunk   = options.dsetDimsChunk;
        info.dsetSlab    = options.dsetSlab;
        info.h5Type      = options.h5Type;
        info.h5Layout    = options.h5Layout;
        info.resizeMode  = options.resizeMode;
        info.compression = options.compression;
        if constexpr(std::is_pointer_v<DataType>) {
            if(not info.dsetDims)
                throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                      "Dimensions for new dataset must be specified for pointer data of type [{}]",
                                                      info.dsetPath.value(),
                                                      h5pp::type::sfinae::type_name<DataType>()));
        }

        if(info.dsetChunk) {
            // If dsetDimsChunk has been given then the layout is supposed to be chunked
            if(not info.h5Layout) info.h5Layout = H5D_CHUNKED;

            // Check that chunking options are sane
            if(info.dsetDims and info.dsetDims->size() != info.dsetChunk->size())
                throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                      "Dataset and chunk dimensions must be the same size: "
                                                      "dset dims {} | chunk dims {}",
                                                      info.dsetPath.value(),
                                                      info.dsetDims.value(),
                                                      info.dsetChunk.value()));

            if(info.h5Layout != H5D_CHUNKED)
                throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                      "Dataset chunk dimensions {} requires H5D_CHUNKED layout",
                                                      info.dsetPath.value(),
                                                      info.dsetChunk.value()));
        }

        // If dsetDimsMax has been given and any of them is H5S_UNLIMITED then the layout is supposed to be chunked
        if(info.dsetDimsMax) {
            // If dsetDimsMax has been given then the layout is supposed to be chunked
            if(not info.h5Layout) info.h5Layout = H5D_CHUNKED;
            if(info.h5Layout != H5D_CHUNKED)
                throw std::runtime_error(h5pp::format("Error creating metadata for new dataset [{}]: "
                                                      "Dataset max dimensions {} requires H5D_CHUNKED layout",
                                                      info.dsetPath.value(),
                                                      info.dsetDimsMax.value()));
        }

        // Next infer the missing properties
        /* clang-format off */
        if(not info.dsetDims)    info.dsetDims      = h5pp::util::getDimensions(data);
        if(not info.h5Type)      info.h5Type        = h5pp::util::getH5Type<DataType>();
        if(not info.dsetSize)    info.dsetSize      = h5pp::util::getSizeFromDimensions(info.dsetDims.value());
        if(not info.dsetRank)    info.dsetRank      = h5pp::util::getRankFromDimensions(info.dsetDims.value());
        if(not info.dsetByte)    info.dsetByte      = h5pp::util::getBytesTotal(data,info.dsetSize);
        if(not info.h5Layout)    info.h5Layout      = h5pp::util::decideLayout(data,info.dsetDims, info.dsetDimsMax);
        if(not info.dsetDimsMax) info.dsetDimsMax   = h5pp::util::decideDimensionsMax(info.dsetDims.value(), info.h5Layout);
        if(not info.dsetChunk)   info.dsetChunk     = h5pp::util::getChunkDimensions(h5pp::util::getBytesPerElem<DataType>(), info.dsetDims.value(),info.dsetDimsMax, info.h5Layout);
        if(not info.compression) info.compression   = h5pp::hdf5::getValidCompressionLevel(info.compression);
        if(not info.resizeMode) {
            if(info.h5Layout != H5D_CHUNKED)
                info.resizeMode = h5pp::ResizeMode::DO_NOT_RESIZE;
            else
                info.resizeMode = h5pp::ResizeMode::RESIZE_TO_FIT;
        }

        h5pp::hdf5::setStringSize<DataType>(data, info.h5Type.value(), info.dsetSize.value(), info.dsetByte.value(), info.dsetDims.value());       // String size will be H5T_VARIABLE unless explicitly specified
        /* clang-format on */
        info.h5Space           = h5pp::util::getDsetSpace(info.dsetSize.value(), info.dsetDims.value(), info.h5Layout.value(), info.dsetDimsMax);
        info.h5PlistDsetCreate = H5Pcreate(H5P_DATASET_CREATE);
        info.h5PlistDsetAccess = H5Pcreate(H5P_DATASET_ACCESS);
        h5pp::hdf5::setProperty_layout(info);    // Must go before setting chunk dims
        h5pp::hdf5::setProperty_chunkDims(info); // Will nullify chunkdims if not H5D_CHUNKED
        h5pp::hdf5::setProperty_compression(info);
        h5pp::hdf5::setSpaceExtent(info);

        // Get c++ properties
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType(info.h5Type.value());

        h5pp::logger::log->trace("Created metadata {}", info.string());
        auto error_msg = h5pp::debug::reportCompatibility(info.h5Layout, info.dsetDims, info.dsetChunk, info.dsetDimsMax);
        if(not error_msg.empty()) throw std::runtime_error(h5pp::format("Created dataset metadata is not well defined: \n{}", error_msg));
        return info;
    }

    template<typename DataType>
    inline void fillDataInfo(const DataType &data, DataInfo &info, const Options &options = Options()) {
        h5pp::logger::log->debug("Scanning metadata of datatype [{}]", h5pp::type::sfinae::type_name<DataType>());
        // The point of passing options is to reinterpret the shape of the data and not to resize!
        // The data container should already be resized before entering this function.

        // First copy the relevant options
        if(not info.dataDims) info.dataDims = options.dataDims;
        if(not info.dataSlab) info.dataSlab = options.dataSlab;

        // Then set the missing information
        if constexpr(std::is_pointer_v<DataType>)
            if(not info.dataDims)
                throw std::runtime_error(
                    h5pp::format("Error deducing data info: Dimensions must be specified for pointer data of type [{}]", h5pp::type::sfinae::type_name<DataType>()));

        // Let the dataDims inform the rest of the inference process
        if(not info.dataDims) info.dataDims = h5pp::util::getDimensions(data); // Will fail if no dataDims passed on a pointer
        if(not info.dataSize) info.dataSize = h5pp::util::getSizeFromDimensions(info.dataDims.value());
        if(not info.dataRank) info.dataRank = h5pp::util::getRankFromDimensions(info.dataDims.value());
        if(not info.dataByte) info.dataByte = info.dataSize.value() * h5pp::util::getBytesPerElem<DataType>();
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType<DataType>();
        h5pp::util::setStringSize<DataType>(
            data, info.dataSize.value(), info.dataByte.value(), info.dataDims.value()); // String size will be H5T_VARIABLE unless explicitly specified
        if(not info.h5Space) info.h5Space = h5pp::util::getMemSpace(info.dataSize.value(), info.dataDims.value());
        h5pp::logger::log->trace("Scanned metadata {}", info.string());
    }

    template<typename DataType>
    inline h5pp::DataInfo getDataInfo(const DataType &data, const Options &options = Options()) {
        h5pp::DataInfo dataInfo;
        // As long as the two selections have the same number of elements, the data can be transferred
        fillDataInfo(data, dataInfo, options);
        return dataInfo;
    }

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline void fillAttrInfo(AttrInfo &info, const h5x &loc, const Options &options, const PropertyLists &plists = PropertyLists()) {
        /* clang-format off */
        if(not options.linkPath) throw std::runtime_error("Could not fill attribute info: No link path was given in options");
        if(not options.attrName) throw std::runtime_error("Could not fill attribute info: No attribute name was given in options");
        if(not info.linkPath)    info.linkPath      = h5pp::util::safe_str(options.linkPath.value());
        if(not info.attrName)    info.attrName      = h5pp::util::safe_str(options.attrName.value());
        if(not info.attrSlab)    info.attrSlab      = options.attrSlab;
        h5pp::logger::log->debug("Scanning metadata of attribute [{}] in link [{}]", info.attrName.value(), info.linkPath.value());
        if(not info.linkExists)  info.linkExists = h5pp::hdf5::checkIfLinkExists(loc, info.linkPath.value(), std::nullopt, plists.linkAccess);
        // If the dataset does not exist, there isn't much else to do so we return;
        if(info.linkExists and not info.linkExists.value()) return;
        // From here on the link exists
        if(not info.h5Link)     info.h5Link       = h5pp::hdf5::openLink<hid::h5o>(loc, info.linkPath.value(), info.linkExists, plists.linkAccess);
        if(not info.attrExists)
            info.attrExists = h5pp::hdf5::checkIfAttributeExists(info.h5Link.value(), info.linkPath.value(), info.attrName.value(), std::nullopt, plists.linkAccess);
        if(info.attrExists and not info.attrExists.value()) return;
        // From here on the attribute exists
        if(not info.h5Attr)    info.h5Attr        = H5Aopen_name(info.h5Link.value(), h5pp::util::safe_str(info.attrName.value()).c_str());
        if(not info.h5Type)    info.h5Type        = H5Aget_type(info.h5Attr.value());
        if(not info.h5Space)   info.h5Space = H5Aget_space(info.h5Attr.value());
        // Get the properties of the selected space
        if(not info.attrByte)   info.attrByte       = h5pp::hdf5::getBytesTotal(info.h5Attr.value(), info.h5Space, info.h5Type);
        if(not info.attrSize)   info.attrSize       = h5pp::hdf5::getSize(info.h5Space.value());
        if(not info.attrDims)   info.attrDims       = h5pp::hdf5::getDimensions(info.h5Space.value());
        if(not info.attrRank)   info.attrRank       = h5pp::hdf5::getRank(info.h5Space.value());
        if(not info.h5PlistAttrCreate) info.h5PlistAttrCreate = H5Aget_create_plist(info.h5Attr.value());
            /* clang-format on */
#if H5_VERSION_GE(1, 10, 0)
        if(not info.h5PlistAttrAccess) info.h5PlistAttrAccess = H5Pcreate(H5P_ATTRIBUTE_ACCESS);
#else
        if(not info.h5PlistAttrAccess) info.h5PlistAttrAccess = H5Pcreate(H5P_ATTRIBUTE_CREATE); // Missing access property in HDF5 1.8.x
#endif
        // Get c++ properties
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType(info.h5Type.value());

        h5pp::logger::log->trace("Scanned metadata {}", info.string());
    }

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline h5pp::AttrInfo readAttrInfo(const h5x &loc, const Options &options, const PropertyLists &plists = PropertyLists()) {
        h5pp::AttrInfo info;
        fillAttrInfo(info, loc, options, plists);
        return info;
    }

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline h5pp::AttrInfo getAttrInfo(const h5x &loc, const Options &options, const PropertyLists &plists = PropertyLists()) {
        auto info = readAttrInfo(loc, options, plists);
        if(info.attrExists.value()) return info;
        h5pp::logger::log->debug("Creating new attribute info for [{}] at link [{}]", options.attrName.value(), options.linkPath.value());

        // First copy the parameters given in options
        if(not info.attrDims) info.attrDims = options.dataDims;
        if(not info.attrSlab) info.attrSlab = options.attrSlab;
        if(not info.h5Type) info.h5Type = options.h5Type;

        // Some sanity checks
        if(not info.attrDims)
            throw std::runtime_error(h5pp::format("Error creating info for attribute [{}] in link [{}]: "
                                                  "Dimensions for new attribute must be specified when no data is given",
                                                  info.attrName.value(),
                                                  info.linkPath.value()));
        if(not info.h5Type)
            throw std::runtime_error(h5pp::format("Error creating info for attribute [{}] in link [{}]: "
                                                  "The HDF5 type for a new dataset must be specified when no data is given",
                                                  info.attrName.value(),
                                                  info.linkPath.value()));

        // Next we infer the missing properties
        if(not info.attrSize) info.attrSize = h5pp::util::getSizeFromDimensions(info.attrDims.value());
        if(not info.attrRank) info.attrRank = h5pp::util::getRankFromDimensions(info.attrDims.value());
        if(not info.attrByte) info.attrByte = info.attrSize.value() * h5pp::hdf5::getBytesPerElem(info.h5Type.value());
        if(not info.h5Space) info.h5Space = h5pp::util::getDsetSpace(info.attrSize.value(), info.attrDims.value(), H5D_COMPACT);

        info.h5PlistAttrCreate = H5Pcreate(H5P_ATTRIBUTE_CREATE);
#if H5_VERSION_GE(1, 10, 0)
        info.h5PlistAttrAccess = H5Pcreate(H5P_ATTRIBUTE_ACCESS);
#else
        info.h5PlistAttrAccess = H5Pcreate(H5P_ATTRIBUTE_CREATE); // Missing access property in HDF5 1.8.x
#endif
        // Get c++ properties
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType(info.h5Type.value());

        h5pp::logger::log->trace("Created  metadata  {}", info.string());
        return info;
    }

    template<typename DataType, typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline h5pp::AttrInfo getAttrInfo(const h5x &loc, const DataType &data, const Options &options, const PropertyLists &plists = PropertyLists()) {
        auto info = readAttrInfo(loc, options, plists);
        if(not info.linkExists) throw std::runtime_error(h5pp::format("Could not get attribute info for link [{}]: Link does not exist.", options.linkPath.value()));
        if(not info.linkExists.value()) throw std::runtime_error(h5pp::format("Could not get attribute info for link [{}]: Link does not exist.", options.linkPath.value()));
        if(info.attrExists and info.attrExists.value()) return info;
        h5pp::logger::log->debug("Creating new attribute info for [{}] at link [{}]", options.attrName.value(), options.linkPath.value());

        // First copy the parameters given in options
        if(not info.attrDims) info.attrDims = options.dataDims;
        if(not info.attrSlab) info.attrSlab = options.attrSlab;
        if(not info.h5Type) info.h5Type = options.h5Type;
        // Some sanity checks
        if constexpr(std::is_pointer_v<DataType>) {
            if(not info.attrDims)
                throw std::runtime_error(h5pp::format("Error creating attribute [{}] on link [{}]: Dimensions for new attribute must be specified for pointer data of type [{}]",
                                                      options.attrName.value(),
                                                      options.linkPath.value(),
                                                      h5pp::type::sfinae::type_name<DataType>()));
        }

        // Next infer the missing properties
        /* clang-format off */
        if(not info.attrDims)    info.attrDims      = h5pp::util::getDimensions(data);
        if(not info.h5Type)      info.h5Type        = h5pp::util::getH5Type<DataType>();
        if(not info.attrSize)    info.attrSize      = h5pp::util::getSizeFromDimensions(info.attrDims.value());
        if(not info.attrRank)    info.attrRank      = h5pp::util::getRankFromDimensions(info.attrDims.value());
        if(not info.attrByte)    info.attrByte      = h5pp::util::getBytesTotal(data,info.attrSize);
        h5pp::hdf5::setStringSize<DataType>(data,info.h5Type.value(), info.attrSize.value(), info.attrByte.value(), info.attrDims.value());       // String size will be H5T_VARIABLE unless explicitly specified
        if(not info.h5Space) info.h5Space = h5pp::util::getDsetSpace(info.attrSize.value(), info.attrDims.value(), H5D_COMPACT);
        /* clang-format on */

        info.h5PlistAttrCreate = H5Pcreate(H5P_ATTRIBUTE_CREATE);
#if H5_VERSION_GE(1, 10, 0)
        info.h5PlistAttrAccess = H5Pcreate(H5P_ATTRIBUTE_ACCESS);
#else
        info.h5PlistAttrAccess = H5Pcreate(H5P_ATTRIBUTE_CREATE); // Missing access property in HDF5 1.8.x
#endif
        // Get c++ properties
        std::tie(info.cppTypeIndex, info.cppTypeName, info.cppTypeSize) = h5pp::hdf5::getCppType(info.h5Type.value());

        h5pp::logger::log->trace("Created  metadata  {}", info.string());
        return info;
    }

    template<typename h5x, typename = h5pp::type::sfinae::enable_if_is_h5_loc<h5x>>
    inline TableInfo getTableInfo(const h5x &loc, std::string_view tableName, std::optional<bool> tableExists = std::nullopt, const PropertyLists &plists = PropertyLists()) {
        TableInfo info;
        // Copy the name and group name
        info.tablePath      = util::safe_str(tableName);
        info.tableGroupName = "";
        size_t pos          = info.tablePath.value().find_last_of('/');
        if(pos != std::string::npos) info.tableGroupName.value().assign(info.tablePath.value().begin(), info.tablePath.value().begin() + static_cast<long>(pos));

        // Copy the location
        if constexpr(std::is_same_v<h5x, hid::h5f>) info.tableFile = loc;
        if constexpr(std::is_same_v<h5x, hid::h5g>) info.tableGroup = loc;
        if constexpr(std::is_same_v<h5x, hid::h5o>) {
            H5I_type_t type = H5Iget_type(loc);
            if(type == H5I_type_t::H5I_GROUP or type == H5I_type_t::H5I_FILE)
                info.tableObjLoc = loc;
            else
                throw std::runtime_error("Given object type for location is not a group or a file");
        }

        info.tableExists = h5pp::hdf5::checkIfLinkExists(loc, tableName, tableExists, plists.linkAccess);
        if(not info.tableExists.value()) return info;

        info.tableDset = hdf5::openLink<hid::h5d>(loc, tableName, info.tableExists, plists.linkAccess);
        info.tableType = H5Dget_type(info.tableDset.value());
        // Alloate temporaries
        hsize_t n_fields, n_records;
        H5TBget_table_info(loc, util::safe_str(tableName).c_str(), &n_fields, &n_records);
        std::vector<size_t> field_sizes(n_fields);
        std::vector<size_t> field_offsets(n_fields);
        size_t              record_bytes;
        char                table_title[255];
        char **             field_names = new char *[n_fields];
        for(size_t i = 0; i < n_fields; i++) field_names[i] = new char[255];
        H5TBget_field_info(loc, util::safe_str(tableName).c_str(), field_names, field_sizes.data(), field_offsets.data(), &record_bytes);
        H5TBAget_title(info.tableDset.value(), table_title);
        // Copy results
        std::vector<std::string> field_names_vec(n_fields);
        std::vector<hid::h5t>    field_types(n_fields);
        for(size_t i = 0; i < n_fields; i++) field_names_vec[i] = field_names[i];
        for(size_t i = 0; i < n_fields; i++) field_types[i] = H5Tget_member_type(info.tableType.value(), static_cast<unsigned>(i));

        info.tableTitle   = table_title;
        info.numFields    = n_fields;
        info.numRecords   = n_records;
        info.recordBytes  = record_bytes;
        info.fieldSizes   = field_sizes;
        info.fieldOffsets = field_offsets;
        info.fieldTypes   = field_types;
        info.fieldNames   = field_names_vec;
        hid::h5p plist    = H5Dget_create_plist(info.tableDset->value());
        auto     chunkVec = h5pp::hdf5::getChunkDimensions(plist);
        if(chunkVec and not chunkVec->empty()) info.chunkSize = chunkVec.value()[0];

        /* release array of char arrays */
        for(size_t i = 0; i < n_fields; i++) delete[] field_names[i];
        delete[] field_names;

        // Get c++ properties
        info.cppTypeIndex = std::vector<std::type_index>();
        info.cppTypeName  = std::vector<std::string>();
        info.cppTypeSize  = std::vector<size_t>();
        for(size_t i = 0; i < n_fields; i++) {
            auto cppInfo = h5pp::hdf5::getCppType(info.fieldTypes.value()[i]);
            info.cppTypeIndex->emplace_back(std::get<0>(cppInfo));
            info.cppTypeName->emplace_back(std::get<1>(cppInfo));
            info.cppTypeSize->emplace_back(std::get<2>(cppInfo));
        }

        return info;
    }

    inline h5pp::TableInfo newTableInfo(const hid::h5t &                  tableType,
                                        std::string_view                  tablePath,
                                        std::string_view                  tableTitle,
                                        const std::optional<hsize_t>      desiredChunkSize        = std::nullopt,
                                        const std::optional<unsigned int> desiredCompressionLevel = std::nullopt) {
        TableInfo info;
        info.tableType      = tableType;
        info.tableTitle     = tableTitle;
        info.tablePath      = tablePath;
        info.tableGroupName = "";
        size_t pos          = info.tablePath.value().find_last_of('/');
        if(pos != std::string::npos)
            info.tableGroupName.value().assign(info.tablePath.value().begin(), info.tablePath.value().begin() + static_cast<std::string::difference_type>(pos));

        info.numFields        = H5Tget_nmembers(tableType);
        info.numRecords       = 0;
        info.recordBytes      = H5Tget_size(info.tableType.value());
        info.chunkSize        = desiredChunkSize.has_value() ? desiredChunkSize.value()
                                                             : h5pp::util::getChunkDimensions(info.recordBytes.value(), {1}, std::nullopt, H5D_layout_t::H5D_CHUNKED).value()[0];
        info.compressionLevel = h5pp::hdf5::getValidCompressionLevel(desiredCompressionLevel);

        info.fieldTypes   = std::vector<h5pp::hid::h5t>();
        info.fieldOffsets = std::vector<size_t>();
        info.fieldSizes   = std::vector<size_t>();
        info.fieldNames   = std::vector<std::string>();

        for(unsigned int idx = 0; idx < static_cast<unsigned int>(info.numFields.value()); idx++) {
            info.fieldTypes.value().emplace_back(H5Tget_member_type(info.tableType.value(), idx));
            info.fieldOffsets.value().emplace_back(H5Tget_member_offset(info.tableType.value(), idx));
            info.fieldSizes.value().emplace_back(H5Tget_size(info.fieldTypes.value().back()));
            const char *name = H5Tget_member_name(info.tableType.value(), idx);
            info.fieldNames.value().emplace_back(name);
            H5free_memory((void *) name);
        }

        // Get c++ properties
        info.cppTypeIndex = std::vector<std::type_index>();
        info.cppTypeName  = std::vector<std::string>();
        info.cppTypeSize  = std::vector<size_t>();
        for(size_t idx = 0; idx < info.numFields.value(); idx++) {
            auto cppInfo = h5pp::hdf5::getCppType(info.fieldTypes.value()[idx]);
            info.cppTypeIndex->emplace_back(std::get<0>(cppInfo));
            info.cppTypeName->emplace_back(std::get<1>(cppInfo));
            info.cppTypeSize->emplace_back(std::get<2>(cppInfo));
        }

        return info;
    }
}