LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5multidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1490 1665 89.5 %
Date: 2026-11-02 03:43:02 Functions: 66 70 94.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  HDF5 read Driver
       4             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include <numeric>
      13             : #ifdef _POSIX_C_SOURCE
      14             : #undef _POSIX_C_SOURCE
      15             : #endif
      16             : 
      17             : #include "hdf5dataset.h"
      18             : #include "hdf5eosparser.h"
      19             : #include "s100.h"
      20             : 
      21             : #include "cpl_float.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <cinttypes>
      25             : #include <limits>
      26             : #include <set>
      27             : #include <utility>
      28             : 
      29             : #if (defined(H5_VERS_MAJOR) &&                                                 \
      30             :      (H5_VERS_MAJOR >= 2 || (H5_VERS_MAJOR == 1 && H5_VERS_MINOR >= 14) ||     \
      31             :       (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 12 && H5_VERS_RELEASE >= 3) ||   \
      32             :       (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 10 && H5_VERS_RELEASE >= 9)))
      33             : #define HAVE_H5Dchunk_iter
      34             : #endif
      35             : 
      36             : namespace GDAL
      37             : {
      38             : 
      39             : /************************************************************************/
      40             : /*                              HDF5Group                               */
      41             : /************************************************************************/
      42             : 
      43             : class HDF5Group final : public GDALGroup
      44             : {
      45             :     std::shared_ptr<HDF5SharedResources> m_poShared;
      46             :     hid_t m_hGroup;
      47             :     std::set<std::pair<unsigned long, unsigned long>> m_oSetParentIds{};
      48             :     const bool m_bIsEOSGridGroup;
      49             :     const bool m_bIsEOSSwathGroup;
      50             :     mutable std::shared_ptr<GDALMDArray> m_poXIndexingArray{};
      51             :     mutable std::shared_ptr<GDALMDArray> m_poYIndexingArray{};
      52             :     mutable std::vector<std::string> m_osListSubGroups{};
      53             :     mutable std::vector<std::string> m_osListArrays{};
      54             :     mutable std::vector<std::shared_ptr<GDALAttribute>> m_oListAttributes{};
      55             :     mutable bool m_bShowAllAttributes = false;
      56             :     mutable bool m_bGotDims = false;
      57             :     mutable std::vector<std::shared_ptr<GDALDimension>> m_cachedDims{};
      58             : 
      59             :     static herr_t GetGroupNamesCallback(hid_t hGroup, const char *pszObjName,
      60             :                                         void *);
      61             : 
      62             :     static herr_t GetArrayNamesCallback(hid_t hGroup, const char *pszObjName,
      63             :                                         void *);
      64             : 
      65             :     static herr_t GetAttributesCallback(hid_t hGroup, const char *pszObjName,
      66             :                                         void *);
      67             : 
      68             :   protected:
      69        2220 :     HDF5Group(
      70             :         const std::string &osParentName, const std::string &osName,
      71             :         const std::shared_ptr<HDF5SharedResources> &poShared,
      72             :         const std::set<std::pair<unsigned long, unsigned long>> &oSetParentIds,
      73             :         hid_t hGroup, unsigned long objIds[2])
      74        2220 :         : GDALGroup(osParentName, osName), m_poShared(poShared),
      75             :           m_hGroup(hGroup), m_oSetParentIds(oSetParentIds),
      76        2220 :           m_bIsEOSGridGroup(osParentName == "/HDFEOS/GRIDS"),
      77        4440 :           m_bIsEOSSwathGroup(osParentName == "/HDFEOS/SWATHS")
      78             :     {
      79        2220 :         m_oSetParentIds.insert(std::pair(objIds[0], objIds[1]));
      80             : 
      81             :         // Force registration of EOS dimensions
      82        2220 :         if (m_bIsEOSGridGroup || m_bIsEOSSwathGroup)
      83             :         {
      84           9 :             HDF5Group::GetDimensions();
      85             :         }
      86        2220 :     }
      87             : 
      88             :   public:
      89        2220 :     static std::shared_ptr<HDF5Group> Create(
      90             :         const std::string &osParentName, const std::string &osName,
      91             :         const std::shared_ptr<HDF5SharedResources> &poShared,
      92             :         const std::set<std::pair<unsigned long, unsigned long>> &oSetParentIds,
      93             :         hid_t hGroup, unsigned long objIds[2])
      94             :     {
      95             :         auto poGroup = std::shared_ptr<HDF5Group>(new HDF5Group(
      96        2220 :             osParentName, osName, poShared, oSetParentIds, hGroup, objIds));
      97        2220 :         poGroup->SetSelf(poGroup);
      98        2220 :         return poGroup;
      99             :     }
     100             : 
     101        4440 :     ~HDF5Group() override
     102        2220 :     {
     103        2220 :         H5Gclose(m_hGroup);
     104        4440 :     }
     105             : 
     106         354 :     hid_t GetID() const
     107             :     {
     108         354 :         return m_hGroup;
     109             :     }
     110             : 
     111             :     std::vector<std::shared_ptr<GDALDimension>>
     112             :     GetDimensions(CSLConstList papszOptions = nullptr) const override;
     113             : 
     114             :     std::vector<std::string>
     115             :     GetGroupNames(CSLConstList papszOptions) const override;
     116             :     std::shared_ptr<GDALGroup> OpenGroup(const std::string &osName,
     117             :                                          CSLConstList) const override;
     118             : 
     119             :     std::vector<std::string>
     120             :     GetMDArrayNames(CSLConstList papszOptions) const override;
     121             :     std::shared_ptr<GDALMDArray>
     122             :     OpenMDArray(const std::string &osName,
     123             :                 CSLConstList papszOptions) const override;
     124             : 
     125             :     std::vector<std::shared_ptr<GDALAttribute>>
     126             :     GetAttributes(CSLConstList papszOptions = nullptr) const override;
     127             : };
     128             : 
     129             : /************************************************************************/
     130             : /*                            HDF5Dimension                             */
     131             : /************************************************************************/
     132             : 
     133             : class HDF5Dimension final : public GDALDimension
     134             : {
     135             :     std::string m_osGroupFullname;
     136             :     std::shared_ptr<HDF5SharedResources> m_poShared;
     137             : 
     138             :   public:
     139          51 :     HDF5Dimension(const std::string &osParentName, const std::string &osName,
     140             :                   const std::string &osType, const std::string &osDirection,
     141             :                   GUInt64 nSize,
     142             :                   const std::shared_ptr<HDF5SharedResources> &poShared)
     143          51 :         : GDALDimension(osParentName, osName, osType, osDirection, nSize),
     144          51 :           m_osGroupFullname(osParentName), m_poShared(poShared)
     145             :     {
     146          51 :     }
     147             : 
     148             :     std::shared_ptr<GDALMDArray> GetIndexingVariable() const override;
     149             : };
     150             : 
     151             : /************************************************************************/
     152             : /*                           BuildDataType()                            */
     153             : /************************************************************************/
     154             : 
     155             : static GDALExtendedDataType
     156      183273 : BuildDataType(hid_t hDataType, bool &bHasString, bool &bNonNativeDataType,
     157             :               const std::vector<std::pair<std::string, hid_t>> &oTypes)
     158             : {
     159      183273 :     const auto klass = H5Tget_class(hDataType);
     160      183273 :     GDALDataType eDT = ::HDF5Dataset::GetDataType(hDataType);
     161      183273 :     if (eDT != GDT_Unknown)
     162             :     {
     163             : #ifdef HDF5_HAVE_FLOAT16
     164             :         if (H5Tequal(hDataType, H5T_NATIVE_FLOAT16) ||
     165             :             HDF5Dataset::IsNativeCFloat16(hDataType))
     166             :         {
     167             :             bNonNativeDataType = true;
     168             :         }
     169             : #endif
     170      133358 :         return GDALExtendedDataType::Create(eDT);
     171             :     }
     172       49915 :     else if (klass == H5T_STRING)
     173             :     {
     174       31449 :         bHasString = true;
     175       31449 :         return GDALExtendedDataType::CreateString();
     176             :     }
     177       18466 :     else if (klass == H5T_COMPOUND)
     178             :     {
     179        1453 :         const unsigned nMembers = H5Tget_nmembers(hDataType);
     180        2906 :         std::vector<std::unique_ptr<GDALEDTComponent>> components;
     181        1453 :         size_t nOffset = 0;
     182        6770 :         for (unsigned i = 0; i < nMembers; i++)
     183             :         {
     184        5593 :             char *pszName = H5Tget_member_name(hDataType, i);
     185        5593 :             if (!pszName)
     186         276 :                 return GDALExtendedDataType::Create(GDT_Unknown);
     187        5593 :             CPLString osCompName(pszName);
     188        5593 :             H5free_memory(pszName);
     189        5593 :             const auto hMemberType = H5Tget_member_type(hDataType, i);
     190        5593 :             if (hMemberType < 0)
     191           0 :                 return GDALExtendedDataType::Create(GDT_Unknown);
     192             :             const hid_t hNativeMemberType =
     193        5593 :                 H5Tget_native_type(hMemberType, H5T_DIR_ASCEND);
     194             :             auto memberDT = BuildDataType(hNativeMemberType, bHasString,
     195        5593 :                                           bNonNativeDataType, oTypes);
     196        5593 :             H5Tclose(hNativeMemberType);
     197        5593 :             H5Tclose(hMemberType);
     198        8934 :             if (memberDT.GetClass() == GEDTC_NUMERIC &&
     199        3341 :                 memberDT.GetNumericDataType() == GDT_Unknown)
     200         276 :                 return GDALExtendedDataType::Create(GDT_Unknown);
     201        5317 :             if ((nOffset % memberDT.GetSize()) != 0)
     202         274 :                 nOffset += memberDT.GetSize() - (nOffset % memberDT.GetSize());
     203        5317 :             if (nOffset != H5Tget_member_offset(hDataType, i))
     204         523 :                 bNonNativeDataType = true;
     205       10634 :             components.emplace_back(std::unique_ptr<GDALEDTComponent>(
     206       10634 :                 new GDALEDTComponent(osCompName, nOffset, memberDT)));
     207        5317 :             nOffset += memberDT.GetSize();
     208             :         }
     209        2354 :         if (!components.empty() &&
     210        1177 :             (nOffset % components[0]->GetType().GetSize()) != 0)
     211         324 :             nOffset += components[0]->GetType().GetSize() -
     212         324 :                        (nOffset % components[0]->GetType().GetSize());
     213        1177 :         if (nOffset != H5Tget_size(hDataType))
     214          85 :             bNonNativeDataType = true;
     215        2354 :         std::string osName("unnamed");
     216        1402 :         for (const auto &oPair : oTypes)
     217             :         {
     218             :             const auto hPairNativeType =
     219         275 :                 H5Tget_native_type(oPair.second, H5T_DIR_ASCEND);
     220         275 :             const auto matches = H5Tequal(hPairNativeType, hDataType);
     221         275 :             H5Tclose(hPairNativeType);
     222         275 :             if (matches)
     223             :             {
     224          50 :                 osName = oPair.first;
     225          50 :                 break;
     226             :             }
     227             :         }
     228             :         return GDALExtendedDataType::Create(osName, nOffset,
     229        1177 :                                             std::move(components));
     230             :     }
     231       17013 :     else if (klass == H5T_ENUM)
     232             :     {
     233       16571 :         const auto hParent = H5Tget_super(hDataType);
     234       16571 :         const hid_t hNativeParent = H5Tget_native_type(hParent, H5T_DIR_ASCEND);
     235             :         auto ret(BuildDataType(hNativeParent, bHasString, bNonNativeDataType,
     236       33142 :                                oTypes));
     237       16571 :         H5Tclose(hNativeParent);
     238       16571 :         H5Tclose(hParent);
     239       16571 :         return ret;
     240             :     }
     241             :     else
     242             :     {
     243         442 :         return GDALExtendedDataType::Create(GDT_Unknown);
     244             :     }
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                        GetDataTypesInGroup()                         */
     249             : /************************************************************************/
     250             : 
     251             : static void
     252         748 : GetDataTypesInGroup(hid_t hHDF5, const std::string &osGroupFullName,
     253             :                     std::vector<std::pair<std::string, hid_t>> &oTypes)
     254             : {
     255             :     struct Callback
     256             :     {
     257       10744 :         static herr_t f(hid_t hGroup, const char *pszObjName, void *user_data)
     258             :         {
     259       10744 :             auto *poTypes =
     260             :                 static_cast<std::vector<std::pair<std::string, hid_t>> *>(
     261             :                     user_data);
     262             :             H5G_stat_t oStatbuf;
     263             : 
     264       10744 :             if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
     265           0 :                 return -1;
     266             : 
     267       10744 :             if (oStatbuf.type == H5G_TYPE)
     268             :             {
     269        2007 :                 poTypes->push_back(
     270        4014 :                     std::pair(pszObjName, H5Topen(hGroup, pszObjName)));
     271             :             }
     272             : 
     273       10744 :             return 0;
     274             :         }
     275             :     };
     276             : 
     277         748 :     H5Giterate(hHDF5, osGroupFullName.c_str(), nullptr, &(Callback::f),
     278             :                &oTypes);
     279         748 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                              HDF5Array                               */
     283             : /************************************************************************/
     284             : 
     285             : class HDF5Array final : public GDALMDArray
     286             : {
     287             :   public:
     288             :     struct BlockInfo
     289             :     {
     290             :         uint64_t nOffset = 0;
     291             :         uint64_t nSize = 0;
     292             :         uint32_t nFilterMask = 0;
     293             :     };
     294             : 
     295             :   private:
     296             :     std::string m_osGroupFullname;
     297             :     std::shared_ptr<HDF5SharedResources> m_poShared;
     298             :     hid_t m_hArray;
     299             :     hid_t m_hDataSpace;
     300             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
     301             :     GDALExtendedDataType m_dt = GDALExtendedDataType::Create(GDT_Unknown);
     302             :     hid_t m_hNativeDT = H5I_INVALID_HID;
     303             :     mutable std::vector<std::shared_ptr<GDALAttribute>> m_oListAttributes{};
     304             :     mutable bool m_bShowAllAttributes = false;
     305             :     bool m_bHasString = false;
     306             :     bool m_bHasNonNativeDataType = false;
     307             :     mutable bool m_bWarnedNoData = false;
     308             :     mutable std::vector<GByte> m_abyNoData{};
     309             :     mutable std::string m_osUnit{};
     310             :     mutable bool m_bHasDimensionList = false;
     311             :     mutable bool m_bHasDimensionLabels = false;
     312             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
     313             :     haddr_t m_nOffset;
     314             :     mutable CPLStringList m_aosStructuralInfo{};
     315             : 
     316             : #ifdef HAVE_H5Dchunk_iter
     317             :     mutable bool m_bFirstBlockInfo = true;
     318             :     mutable std::vector<BlockInfo> m_asBlockInfo{};
     319             : #endif
     320             : 
     321             :     HDF5Array(const std::string &osParentName, const std::string &osName,
     322             :               const std::shared_ptr<HDF5SharedResources> &poShared,
     323             :               hid_t hArray, const HDF5Group *poGroup,
     324             :               bool bSkipFullDimensionInstantiation);
     325             : 
     326             :     void InstantiateDimensions(const std::string &osParentName,
     327             :                                const HDF5Group *poGroup);
     328             : 
     329             :     bool ReadSlow(const GUInt64 *arrayStartIdx, const size_t *count,
     330             :                   const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     331             :                   const GDALExtendedDataType &bufferDataType,
     332             :                   void *pDstBuffer) const;
     333             : 
     334             :     static herr_t GetAttributesCallback(hid_t hArray, const char *pszObjName,
     335             :                                         void *);
     336             : 
     337             :   protected:
     338             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     339             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     340             :                const GDALExtendedDataType &bufferDataType,
     341             :                void *pDstBuffer) const override;
     342             : 
     343             :   public:
     344             :     ~HDF5Array() override;
     345             : 
     346             :     static std::shared_ptr<HDF5Array>
     347        2048 :     Create(const std::string &osParentName, const std::string &osName,
     348             :            const std::shared_ptr<HDF5SharedResources> &poShared, hid_t hArray,
     349             :            const HDF5Group *poGroup, bool bSkipFullDimensionInstantiation)
     350             :     {
     351             :         HDF5_GLOBAL_LOCK();
     352             : 
     353             :         auto ar(std::shared_ptr<HDF5Array>(
     354             :             new HDF5Array(osParentName, osName, poShared, hArray, poGroup,
     355        4096 :                           bSkipFullDimensionInstantiation)));
     356        2763 :         if (ar->m_dt.GetClass() == GEDTC_NUMERIC &&
     357         715 :             ar->m_dt.GetNumericDataType() == GDT_Unknown)
     358             :         {
     359           0 :             return nullptr;
     360             :         }
     361        2048 :         ar->SetSelf(ar);
     362        2048 :         return ar;
     363             :     }
     364             : 
     365         209 :     bool IsWritable() const override
     366             :     {
     367         209 :         return !m_poShared->IsReadOnly();
     368             :     }
     369             : 
     370        1836 :     const std::string &GetFilename() const override
     371             :     {
     372        1836 :         return m_poShared->GetFilename();
     373             :     }
     374             : 
     375             :     const std::vector<std::shared_ptr<GDALDimension>> &
     376        4243 :     GetDimensions() const override
     377             :     {
     378        4243 :         return m_dims;
     379             :     }
     380             : 
     381        3675 :     const GDALExtendedDataType &GetDataType() const override
     382             :     {
     383        3675 :         return m_dt;
     384             :     }
     385             : 
     386             :     std::shared_ptr<GDALAttribute>
     387             :     GetAttribute(const std::string &osName) const override;
     388             : 
     389             :     std::vector<std::shared_ptr<GDALAttribute>>
     390             :     GetAttributes(CSLConstList papszOptions = nullptr) const override;
     391             : 
     392             :     std::vector<GUInt64> GetBlockSize() const override;
     393             : 
     394             :     CSLConstList GetStructuralInfo() const override;
     395             : 
     396         129 :     const void *GetRawNoDataValue() const override
     397             :     {
     398         129 :         return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
     399             :     }
     400             : 
     401          20 :     const std::string &GetUnit() const override
     402             :     {
     403          20 :         return m_osUnit;
     404             :     }
     405             : 
     406             :     haddr_t GetFileOffset() const
     407             :     {
     408             :         return m_nOffset;
     409             :     }
     410             : 
     411          13 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     412             :     {
     413          13 :         return m_poSRS;
     414             :     }
     415             : 
     416             :     std::vector<std::shared_ptr<GDALMDArray>>
     417             :     GetCoordinateVariables() const override;
     418             : 
     419         108 :     std::shared_ptr<GDALGroup> GetRootGroup() const override
     420             :     {
     421         108 :         return m_poShared->GetRootGroup();
     422             :     }
     423             : 
     424             :     bool GetRawBlockInfo(const uint64_t *panBlockCoordinates,
     425             :                          GDALMDArrayRawBlockInfo &info) const override;
     426             : };
     427             : 
     428             : /************************************************************************/
     429             : /*                            HDF5Attribute                             */
     430             : /************************************************************************/
     431             : 
     432             : class HDF5Attribute final : public GDALAttribute
     433             : {
     434             :     std::shared_ptr<HDF5SharedResources> m_poShared;
     435             :     hid_t m_hAttribute;
     436             :     hid_t m_hDataSpace;
     437             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
     438             :     GDALExtendedDataType m_dt = GDALExtendedDataType::Create(GDT_Unknown);
     439             :     hid_t m_hNativeDT = H5I_INVALID_HID;
     440             :     size_t m_nElements = 1;
     441             :     bool m_bHasString = false;
     442             :     bool m_bHasNonNativeDataType = false;
     443             : 
     444      159061 :     HDF5Attribute(const std::string &osGroupFullName,
     445             :                   const std::string &osParentName, const std::string &osName,
     446             :                   const std::shared_ptr<HDF5SharedResources> &poShared,
     447             :                   hid_t hAttribute)
     448      159061 :         : GDALAbstractMDArray(osParentName, osName),
     449             :           GDALAttribute(osParentName, osName), m_poShared(poShared),
     450      159061 :           m_hAttribute(hAttribute), m_hDataSpace(H5Aget_space(hAttribute))
     451             :     {
     452      159061 :         const int nDims = H5Sget_simple_extent_ndims(m_hDataSpace);
     453      318122 :         std::vector<hsize_t> anDimSizes(nDims);
     454      159061 :         if (nDims)
     455             :         {
     456        1366 :             H5Sget_simple_extent_dims(m_hDataSpace, &anDimSizes[0], nullptr);
     457        1981 :             for (int i = 0; i < nDims; ++i)
     458             :             {
     459        1366 :                 m_nElements *= static_cast<size_t>(anDimSizes[i]);
     460        1366 :                 if (nDims == 1 && m_nElements == 1)
     461             :                 {
     462             :                     // Expose 1-dim of size 1 as scalar
     463         751 :                     break;
     464             :                 }
     465        1230 :                 m_dims.emplace_back(std::make_shared<GDALDimension>(
     466        1230 :                     std::string(), CPLSPrintf("dim%d", i), std::string(),
     467        1845 :                     std::string(), anDimSizes[i]));
     468             :             }
     469             :         }
     470             : 
     471      159061 :         const hid_t hDataType = H5Aget_type(hAttribute);
     472      159061 :         m_hNativeDT = H5Tget_native_type(hDataType, H5T_DIR_ASCEND);
     473      159061 :         H5Tclose(hDataType);
     474             : 
     475      318122 :         std::vector<std::pair<std::string, hid_t>> oTypes;
     476      314528 :         if (!osGroupFullName.empty() &&
     477      155467 :             H5Tget_class(m_hNativeDT) == H5T_COMPOUND)
     478             :         {
     479         265 :             GetDataTypesInGroup(m_poShared->GetHDF5(), osGroupFullName, oTypes);
     480             :         }
     481             : 
     482      318122 :         m_dt = BuildDataType(m_hNativeDT, m_bHasString, m_bHasNonNativeDataType,
     483      159061 :                              oTypes);
     484      160690 :         for (auto &oPair : oTypes)
     485        1629 :             H5Tclose(oPair.second);
     486      289081 :         if (m_dt.GetClass() == GEDTC_NUMERIC &&
     487      130020 :             m_dt.GetNumericDataType() == GDT_Unknown)
     488             :         {
     489         442 :             CPLDebug("HDF5",
     490             :                      "Cannot map data type of %s to a type handled by GDAL",
     491             :                      osName.c_str());
     492             :         }
     493      159061 :     }
     494             : 
     495             :   protected:
     496             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     497             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     498             :                const GDALExtendedDataType &bufferDataType,
     499             :                void *pDstBuffer) const override;
     500             : 
     501             :   public:
     502             :     ~HDF5Attribute() override;
     503             : 
     504             :     static std::shared_ptr<HDF5Attribute>
     505      159061 :     Create(const std::string &osGroupFullName, const std::string &osParentName,
     506             :            const std::string &osName,
     507             :            const std::shared_ptr<HDF5SharedResources> &poShared,
     508             :            hid_t hAttribute)
     509             :     {
     510             :         HDF5_GLOBAL_LOCK();
     511             : 
     512             :         auto ar(std::shared_ptr<HDF5Attribute>(new HDF5Attribute(
     513      318122 :             osGroupFullName, osParentName, osName, poShared, hAttribute)));
     514      289081 :         if (ar->m_dt.GetClass() == GEDTC_NUMERIC &&
     515      130020 :             ar->m_dt.GetNumericDataType() == GDT_Unknown)
     516             :         {
     517         442 :             return nullptr;
     518             :         }
     519      158619 :         return ar;
     520             :     }
     521             : 
     522             :     const std::vector<std::shared_ptr<GDALDimension>> &
     523       20437 :     GetDimensions() const override
     524             :     {
     525       20437 :         return m_dims;
     526             :     }
     527             : 
     528       17742 :     const GDALExtendedDataType &GetDataType() const override
     529             :     {
     530       17742 :         return m_dt;
     531             :     }
     532             : };
     533             : 
     534             : /************************************************************************/
     535             : /*                        HDF5SharedResources()                         */
     536             : /************************************************************************/
     537             : 
     538         349 : HDF5SharedResources::HDF5SharedResources(const std::string &osFilename)
     539             :     : m_osFilename(osFilename),
     540         349 :       m_poPAM(std::make_shared<GDALPamMultiDim>(osFilename))
     541             : {
     542         349 : }
     543             : 
     544             : /************************************************************************/
     545             : /*                        ~HDF5SharedResources()                        */
     546             : /************************************************************************/
     547             : 
     548         349 : HDF5SharedResources::~HDF5SharedResources()
     549             : {
     550             :     HDF5_GLOBAL_LOCK();
     551             : 
     552         349 :     if (m_hHDF5 > 0)
     553         349 :         H5Fclose(m_hHDF5);
     554         349 : }
     555             : 
     556             : /************************************************************************/
     557             : /*                               Create()                               */
     558             : /************************************************************************/
     559             : 
     560             : std::shared_ptr<HDF5SharedResources>
     561         349 : HDF5SharedResources::Create(const std::string &osFilename)
     562             : {
     563             :     auto poSharedResources = std::shared_ptr<HDF5SharedResources>(
     564         349 :         new HDF5SharedResources(osFilename));
     565         349 :     poSharedResources->m_poSelf = poSharedResources;
     566         349 :     return poSharedResources;
     567             : }
     568             : 
     569             : /************************************************************************/
     570             : /*                            GetRootGroup()                            */
     571             : /************************************************************************/
     572             : 
     573         951 : std::shared_ptr<HDF5Group> HDF5SharedResources::GetRootGroup()
     574             : {
     575             : 
     576             :     H5G_stat_t oStatbuf;
     577         951 :     if (H5Gget_objinfo(m_hHDF5, "/", FALSE, &oStatbuf) < 0)
     578             :     {
     579           0 :         return nullptr;
     580             :     }
     581         951 :     auto hGroup = H5Gopen(m_hHDF5, "/");
     582         951 :     if (hGroup < 0)
     583             :     {
     584           0 :         return nullptr;
     585             :     }
     586             : 
     587         951 :     auto poSharedResources = m_poSelf.lock();
     588         951 :     CPLAssert(poSharedResources != nullptr);
     589        1902 :     return HDF5Group::Create(std::string(), "/", poSharedResources, {}, hGroup,
     590        2853 :                              oStatbuf.objno);
     591             : }
     592             : 
     593             : /************************************************************************/
     594             : /*                           GetDimensions()                            */
     595             : /************************************************************************/
     596             : 
     597             : std::vector<std::shared_ptr<GDALDimension>>
     598          57 : HDF5Group::GetDimensions(CSLConstList) const
     599             : {
     600             :     HDF5_GLOBAL_LOCK();
     601             : 
     602          57 :     if (m_bGotDims)
     603          25 :         return m_cachedDims;
     604             : 
     605             :     struct CallbackData
     606             :     {
     607             :         std::shared_ptr<HDF5SharedResources> poShared{};
     608             :         std::string osFullName{};
     609             :         std::map<std::string, std::shared_ptr<GDALDimension>> oDimMap{};
     610             :     };
     611             : 
     612             :     struct Callback
     613             :     {
     614         315 :         static herr_t f(hid_t hGroup, const char *pszObjName, void *user_data)
     615             :         {
     616         315 :             CallbackData *data = static_cast<CallbackData *>(user_data);
     617             :             H5G_stat_t oStatbuf;
     618             : 
     619         315 :             if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
     620           0 :                 return -1;
     621             : 
     622         315 :             if (oStatbuf.type == H5G_DATASET)
     623             :             {
     624         239 :                 auto hArray = H5Dopen(hGroup, pszObjName);
     625         239 :                 if (hArray >= 0)
     626             :                 {
     627         239 :                     auto ar = HDF5Array::Create(data->osFullName, pszObjName,
     628         239 :                                                 data->poShared, hArray, nullptr,
     629         478 :                                                 true);
     630         239 :                     if (ar && ar->GetDimensionCount() == 1)
     631             :                     {
     632         146 :                         auto attrCLASS = ar->GetAttribute("CLASS");
     633         138 :                         if (attrCLASS && attrCLASS->GetDimensionCount() == 0 &&
     634          65 :                             attrCLASS->GetDataType().GetClass() == GEDTC_STRING)
     635             :                         {
     636          65 :                             const char *pszStr = attrCLASS->ReadAsString();
     637          65 :                             if (pszStr && EQUAL(pszStr, "DIMENSION_SCALE"))
     638             :                             {
     639          65 :                                 std::string osType;
     640          65 :                                 if (strcmp(pszObjName, "XDim") == 0)
     641           4 :                                     osType = GDAL_DIM_TYPE_HORIZONTAL_X;
     642          61 :                                 else if (strcmp(pszObjName, "YDim") == 0)
     643           4 :                                     osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
     644             : 
     645         130 :                                 auto attrNAME = ar->GetAttribute("NAME");
     646         128 :                                 if (attrNAME &&
     647         128 :                                     attrNAME->GetDimensionCount() == 0 &&
     648          63 :                                     attrNAME->GetDataType().GetClass() ==
     649             :                                         GEDTC_STRING)
     650             :                                 {
     651             :                                     const char *pszName =
     652          63 :                                         attrNAME->ReadAsString();
     653          63 :                                     if (pszName &&
     654          63 :                                         STARTS_WITH(
     655             :                                             pszName,
     656             :                                             "This is a netCDF dimension but "
     657             :                                             "not a netCDF variable"))
     658             :                                     {
     659          72 :                                         data->oDimMap[pszObjName] =
     660          36 :                                             std::make_shared<GDALDimension>(
     661          36 :                                                 data->osFullName, pszObjName,
     662          36 :                                                 osType, std::string(),
     663          36 :                                                 ar->GetDimensions()[0]
     664          72 :                                                     ->GetSize());
     665          36 :                                         return 0;
     666             :                                     }
     667             :                                 }
     668             : 
     669          58 :                                 data->oDimMap[pszObjName] =
     670          29 :                                     std::make_shared<HDF5Dimension>(
     671          29 :                                         data->osFullName, pszObjName, osType,
     672          29 :                                         std::string(),
     673          58 :                                         ar->GetDimensions()[0]->GetSize(),
     674          58 :                                         data->poShared);
     675             :                             }
     676             :                         }
     677             :                     }
     678             :                 }
     679             :             }
     680             : 
     681         279 :             return 0;
     682             :         }
     683             :     };
     684             : 
     685          64 :     CallbackData data;
     686          32 :     data.poShared = m_poShared;
     687          32 :     data.osFullName = GetFullName();
     688          32 :     H5Giterate(m_poShared->GetHDF5(), GetFullName().c_str(), nullptr,
     689             :                &(Callback::f), &data);
     690          32 :     m_bGotDims = true;
     691             : 
     692          32 :     if (m_bIsEOSGridGroup)
     693             :     {
     694           6 :         const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
     695           6 :         HDF5EOSParser::GridMetadata oGridMetadata;
     696          12 :         if (poHDF5EOSParser &&
     697           6 :             poHDF5EOSParser->GetGridMetadata(GetName(), oGridMetadata))
     698             :         {
     699           6 :             GDALGeoTransform gt;
     700           6 :             const bool bHasGT = oGridMetadata.GetGeoTransform(gt) &&
     701           6 :                                 gt.xrot == 0 && gt.yrot == 0;
     702          25 :             for (auto &oDim : oGridMetadata.aoDimensions)
     703             :             {
     704          19 :                 auto iterInDimMap = data.oDimMap.find(oDim.osName);
     705          19 :                 if (iterInDimMap != data.oDimMap.end())
     706             :                 {
     707             :                     auto indexingVar =
     708          16 :                         iterInDimMap->second->GetIndexingVariable();
     709           8 :                     if (indexingVar != nullptr)
     710             :                     {
     711          12 :                         if (oDim.osName == "XDim" && bHasGT &&
     712           4 :                             indexingVar->GetDimensions()[0]->GetSize() > 0)
     713             :                         {
     714           4 :                             const GUInt64 anArrayStartIdx0[] = {0};
     715           4 :                             const size_t anCount[] = {1};
     716           4 :                             const GInt64 anArrayStep[] = {0};
     717           4 :                             const GPtrDiff_t anBufferStride[] = {0};
     718           4 :                             double dfFirstVal = 0;
     719           8 :                             if (indexingVar->Read(
     720             :                                     anArrayStartIdx0, anCount, anArrayStep,
     721             :                                     anBufferStride,
     722           8 :                                     GDALExtendedDataType::Create(GDT_Float64),
     723             :                                     &dfFirstVal))
     724             :                             {
     725           4 :                                 CPLDebug(
     726             :                                     "HDF5",
     727             :                                     "XDim FirstX: (from XDim array:%.17g,) "
     728             :                                     "from HDF5EOS metadata:%.17g",
     729             :                                     dfFirstVal, gt.xorig);
     730             :                             }
     731             : 
     732             :                             const GUInt64 anArrayStartIdx1[] = {
     733           4 :                                 indexingVar->GetDimensions()[0]->GetSize() - 1};
     734           4 :                             double dfLastVal = 0;
     735           8 :                             if (indexingVar->Read(
     736             :                                     anArrayStartIdx1, anCount, anArrayStep,
     737             :                                     anBufferStride,
     738           8 :                                     GDALExtendedDataType::Create(GDT_Float64),
     739             :                                     &dfLastVal))
     740             :                             {
     741           4 :                                 CPLDebug("HDF5",
     742             :                                          "XDim LastX: (from XDim array:%.17g,) "
     743             :                                          "from HDF5EOS metadata:%.17g",
     744             :                                          dfLastVal,
     745           4 :                                          gt.xorig + oDim.nSize * gt.xscale);
     746             :                             }
     747             :                         }
     748           8 :                         else if (oDim.osName == "YDim" && bHasGT &&
     749           4 :                                  indexingVar->GetDimensions()[0]->GetSize() > 0)
     750             :                         {
     751           4 :                             const GUInt64 anArrayStartIdx0[] = {0};
     752           4 :                             const size_t anCount[] = {1};
     753           4 :                             const GInt64 anArrayStep[] = {0};
     754           4 :                             const GPtrDiff_t anBufferStride[] = {0};
     755           4 :                             double dfFirstVal = 0;
     756           8 :                             if (indexingVar->Read(
     757             :                                     anArrayStartIdx0, anCount, anArrayStep,
     758             :                                     anBufferStride,
     759           8 :                                     GDALExtendedDataType::Create(GDT_Float64),
     760             :                                     &dfFirstVal))
     761             :                             {
     762           4 :                                 CPLDebug(
     763             :                                     "HDF5",
     764             :                                     "YDim FirstY: (from YDim array:%.17g,) "
     765             :                                     "from HDF5EOS metadata:%.17g",
     766             :                                     dfFirstVal, gt.yorig);
     767             :                             }
     768             : 
     769             :                             const GUInt64 anArrayStartIdx1[] = {
     770           4 :                                 indexingVar->GetDimensions()[0]->GetSize() - 1};
     771           4 :                             double dfLastVal = 0;
     772           8 :                             if (indexingVar->Read(
     773             :                                     anArrayStartIdx1, anCount, anArrayStep,
     774             :                                     anBufferStride,
     775           8 :                                     GDALExtendedDataType::Create(GDT_Float64),
     776             :                                     &dfLastVal))
     777             :                             {
     778           4 :                                 CPLDebug("HDF5",
     779             :                                          "YDim LastY: (from YDim array:%.17g,) "
     780             :                                          "from HDF5EOS metadata:%.17g",
     781             :                                          dfLastVal,
     782           4 :                                          gt.yorig + oDim.nSize * gt.yscale);
     783             :                             }
     784             :                         }
     785             :                     }
     786             :                 }
     787             : 
     788          19 :                 if (oDim.osName == "XDim" && bHasGT)
     789             :                 {
     790           6 :                     if (iterInDimMap != data.oDimMap.end())
     791           4 :                         data.oDimMap.erase(iterInDimMap);
     792             :                     auto poDim = std::make_shared<GDALDimensionWeakIndexingVar>(
     793           6 :                         GetFullName(), oDim.osName, GDAL_DIM_TYPE_HORIZONTAL_X,
     794          18 :                         std::string(), oDim.nSize);
     795             :                     auto poIndexingVar = GDALMDArrayRegularlySpaced::Create(
     796           6 :                         GetFullName(), oDim.osName, poDim,
     797          12 :                         gt.xorig + gt.xscale / 2, gt.xscale, 0);
     798           6 :                     poDim->SetIndexingVariable(poIndexingVar);
     799           6 :                     m_poXIndexingArray = poIndexingVar;
     800           6 :                     m_poShared->KeepRef(poIndexingVar);
     801           6 :                     m_cachedDims.emplace_back(poDim);
     802             :                 }
     803          13 :                 else if (oDim.osName == "YDim" && bHasGT)
     804             :                 {
     805           6 :                     if (iterInDimMap != data.oDimMap.end())
     806           4 :                         data.oDimMap.erase(iterInDimMap);
     807             :                     auto poDim = std::make_shared<GDALDimensionWeakIndexingVar>(
     808           6 :                         GetFullName(), oDim.osName, GDAL_DIM_TYPE_HORIZONTAL_Y,
     809          18 :                         std::string(), oDim.nSize);
     810             :                     auto poIndexingVar = GDALMDArrayRegularlySpaced::Create(
     811           6 :                         GetFullName(), oDim.osName, poDim,
     812          12 :                         gt.yorig + gt.yscale / 2, gt.yscale, 0);
     813           6 :                     poDim->SetIndexingVariable(poIndexingVar);
     814           6 :                     m_poYIndexingArray = poIndexingVar;
     815           6 :                     m_poShared->KeepRef(poIndexingVar);
     816           6 :                     m_cachedDims.emplace_back(poDim);
     817             :                 }
     818           7 :                 else if (iterInDimMap == data.oDimMap.end())
     819             :                 {
     820          14 :                     m_cachedDims.emplace_back(std::make_shared<GDALDimension>(
     821          14 :                         GetFullName(), oDim.osName, std::string(),
     822          21 :                         std::string(), oDim.nSize));
     823             :                 }
     824             :             }
     825           6 :             for (auto &iter : data.oDimMap)
     826           0 :                 m_cachedDims.emplace_back(std::move(iter.second));
     827           6 :             m_poShared->RegisterEOSGridDimensions(GetName(), m_cachedDims);
     828           6 :             return m_cachedDims;
     829             :         }
     830             :     }
     831          26 :     else if (m_bIsEOSSwathGroup)
     832             :     {
     833           3 :         const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
     834           3 :         HDF5EOSParser::SwathMetadata oSwathMetadata;
     835           6 :         if (poHDF5EOSParser &&
     836           3 :             poHDF5EOSParser->GetSwathMetadata(GetName(), oSwathMetadata))
     837             :         {
     838          12 :             for (auto &oDim : oSwathMetadata.aoDimensions)
     839             :             {
     840           9 :                 auto iterInDimMap = data.oDimMap.find(oDim.osName);
     841           9 :                 if (iterInDimMap != data.oDimMap.end())
     842             :                 {
     843           0 :                     if (iterInDimMap->second->GetIndexingVariable() != nullptr)
     844             :                     {
     845           0 :                         continue;
     846             :                     }
     847           0 :                     data.oDimMap.erase(iterInDimMap);
     848             :                 }
     849             : 
     850          18 :                 m_cachedDims.emplace_back(std::make_shared<GDALDimension>(
     851          18 :                     GetFullName(), oDim.osName, std::string(), std::string(),
     852          18 :                     oDim.nSize));
     853             :             }
     854           3 :             for (auto &iter : data.oDimMap)
     855           0 :                 m_cachedDims.emplace_back(std::move(iter.second));
     856           3 :             m_poShared->RegisterEOSSwathDimensions(GetName(), m_cachedDims);
     857           3 :             return m_cachedDims;
     858             :         }
     859             :     }
     860             : 
     861          80 :     for (auto &iter : data.oDimMap)
     862          57 :         m_cachedDims.emplace_back(std::move(iter.second));
     863          23 :     return m_cachedDims;
     864             : }
     865             : 
     866             : /************************************************************************/
     867             : /*                       GetGroupNamesCallback()                        */
     868             : /************************************************************************/
     869             : 
     870        2942 : herr_t HDF5Group::GetGroupNamesCallback(hid_t hGroup, const char *pszObjName,
     871             :                                         void *selfIn)
     872             : {
     873        2942 :     HDF5Group *self = static_cast<HDF5Group *>(selfIn);
     874             :     H5G_stat_t oStatbuf;
     875             : 
     876        2942 :     if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
     877           0 :         return -1;
     878             : 
     879        2942 :     if (oStatbuf.type == H5G_GROUP)
     880             :     {
     881        1718 :         if (self->m_oSetParentIds.find(
     882        1718 :                 std::pair(oStatbuf.objno[0], oStatbuf.objno[1])) ==
     883        3436 :             self->m_oSetParentIds.end())
     884             :         {
     885        1718 :             self->m_osListSubGroups.push_back(pszObjName);
     886             :         }
     887             :         else
     888             :         {
     889           0 :             CPLDebug("HDF5",
     890             :                      "Group %s contains a link to group %s which is "
     891             :                      "itself, or one of its ancestor.",
     892           0 :                      self->GetFullName().c_str(), pszObjName);
     893             :         }
     894             :     }
     895        2942 :     return 0;
     896             : }
     897             : 
     898             : /************************************************************************/
     899             : /*                           GetGroupNames()                            */
     900             : /************************************************************************/
     901             : 
     902        1238 : std::vector<std::string> HDF5Group::GetGroupNames(CSLConstList) const
     903             : {
     904             :     HDF5_GLOBAL_LOCK();
     905             : 
     906        1238 :     m_osListSubGroups.clear();
     907        1238 :     H5Giterate(m_poShared->GetHDF5(), GetFullName().c_str(), nullptr,
     908             :                GetGroupNamesCallback,
     909             :                const_cast<void *>(static_cast<const void *>(this)));
     910        1238 :     return m_osListSubGroups;
     911             : }
     912             : 
     913             : /************************************************************************/
     914             : /*                             OpenGroup()                              */
     915             : /************************************************************************/
     916             : 
     917        1452 : std::shared_ptr<GDALGroup> HDF5Group::OpenGroup(const std::string &osName,
     918             :                                                 CSLConstList) const
     919             : {
     920             :     HDF5_GLOBAL_LOCK();
     921             : 
     922        1452 :     if (m_osListSubGroups.empty())
     923        1110 :         GetGroupNames(nullptr);
     924        1452 :     if (std::find(m_osListSubGroups.begin(), m_osListSubGroups.end(), osName) ==
     925        2904 :         m_osListSubGroups.end())
     926             :     {
     927         183 :         return nullptr;
     928             :     }
     929             : 
     930             :     H5G_stat_t oStatbuf;
     931        1269 :     if (H5Gget_objinfo(m_hGroup, osName.c_str(), FALSE, &oStatbuf) < 0)
     932           0 :         return nullptr;
     933             : 
     934        1269 :     auto hSubGroup = H5Gopen(m_hGroup, osName.c_str());
     935        1269 :     if (hSubGroup < 0)
     936             :     {
     937           0 :         return nullptr;
     938             :     }
     939        2538 :     return HDF5Group::Create(GetFullName(), osName, m_poShared, m_oSetParentIds,
     940        1269 :                              hSubGroup, oStatbuf.objno);
     941             : }
     942             : 
     943             : /************************************************************************/
     944             : /*                       GetArrayNamesCallback()                        */
     945             : /************************************************************************/
     946             : 
     947        1486 : herr_t HDF5Group::GetArrayNamesCallback(hid_t hGroup, const char *pszObjName,
     948             :                                         void *selfIn)
     949             : {
     950        1486 :     HDF5Group *self = static_cast<HDF5Group *>(selfIn);
     951             :     H5G_stat_t oStatbuf;
     952             : 
     953        1486 :     if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
     954           0 :         return -1;
     955             : 
     956        1486 :     if (oStatbuf.type == H5G_DATASET)
     957             :     {
     958        1258 :         auto hArray = H5Dopen(hGroup, pszObjName);
     959        1258 :         if (hArray >= 0)
     960             :         {
     961        2516 :             auto ar(HDF5Array::Create(std::string(), pszObjName,
     962        3774 :                                       self->m_poShared, hArray, self, true));
     963        1258 :             if (ar)
     964             :             {
     965        2516 :                 auto attrNAME = ar->GetAttribute("NAME");
     966        1313 :                 if (attrNAME && attrNAME->GetDimensionCount() == 0 &&
     967          55 :                     attrNAME->GetDataType().GetClass() == GEDTC_STRING)
     968             :                 {
     969          55 :                     const char *pszName = attrNAME->ReadAsString();
     970          55 :                     if (pszName &&
     971          55 :                         STARTS_WITH(pszName, "This is a netCDF dimension but "
     972             :                                              "not a netCDF variable"))
     973             :                     {
     974          34 :                         return 0;
     975             :                     }
     976             :                 }
     977             :             }
     978             :         }
     979        1224 :         self->m_osListArrays.push_back(pszObjName);
     980             :     }
     981        1452 :     return 0;
     982             : }
     983             : 
     984             : /************************************************************************/
     985             : /*                          GetMDArrayNames()                           */
     986             : /************************************************************************/
     987             : 
     988         479 : std::vector<std::string> HDF5Group::GetMDArrayNames(CSLConstList) const
     989             : {
     990             :     HDF5_GLOBAL_LOCK();
     991             : 
     992         479 :     m_osListArrays.clear();
     993         479 :     H5Giterate(m_poShared->GetHDF5(), GetFullName().c_str(), nullptr,
     994             :                GetArrayNamesCallback,
     995             :                const_cast<void *>(static_cast<const void *>(this)));
     996             : 
     997         479 :     if (m_poXIndexingArray)
     998           0 :         m_osListArrays.push_back(m_poXIndexingArray->GetName());
     999         479 :     if (m_poYIndexingArray)
    1000           0 :         m_osListArrays.push_back(m_poYIndexingArray->GetName());
    1001             : 
    1002         479 :     return m_osListArrays;
    1003             : }
    1004             : 
    1005             : /************************************************************************/
    1006             : /*                            OpenMDArray()                             */
    1007             : /************************************************************************/
    1008             : 
    1009         554 : std::shared_ptr<GDALMDArray> HDF5Group::OpenMDArray(const std::string &osName,
    1010             :                                                     CSLConstList) const
    1011             : {
    1012             :     HDF5_GLOBAL_LOCK();
    1013             : 
    1014         554 :     if (m_osListArrays.empty())
    1015         442 :         GetMDArrayNames(nullptr);
    1016         554 :     if (std::find(m_osListArrays.begin(), m_osListArrays.end(), osName) ==
    1017        1108 :         m_osListArrays.end())
    1018             :     {
    1019          23 :         return nullptr;
    1020             :     }
    1021         531 :     if (m_poXIndexingArray && m_poXIndexingArray->GetName() == osName)
    1022           0 :         return m_poXIndexingArray;
    1023         531 :     if (m_poYIndexingArray && m_poYIndexingArray->GetName() == osName)
    1024           0 :         return m_poYIndexingArray;
    1025             : 
    1026         531 :     auto hArray = H5Dopen(m_hGroup, osName.c_str());
    1027         531 :     if (hArray < 0)
    1028             :     {
    1029           0 :         return nullptr;
    1030             :     }
    1031        1062 :     return HDF5Array::Create(GetFullName(), osName, m_poShared, hArray, this,
    1032         531 :                              false);
    1033             : }
    1034             : 
    1035             : /************************************************************************/
    1036             : /*                       GetAttributesCallback()                        */
    1037             : /************************************************************************/
    1038             : 
    1039      154052 : herr_t HDF5Group::GetAttributesCallback(hid_t hGroup, const char *pszObjName,
    1040             :                                         void *selfIn)
    1041             : {
    1042      154052 :     HDF5Group *self = static_cast<HDF5Group *>(selfIn);
    1043      154052 :     if (self->m_bShowAllAttributes || (!EQUAL(pszObjName, "_Netcdf4Dimid") &&
    1044      154052 :                                        !EQUAL(pszObjName, "_NCProperties")))
    1045             :     {
    1046      154050 :         hid_t hAttr = H5Aopen_name(hGroup, pszObjName);
    1047      154050 :         if (hAttr > 0)
    1048             :         {
    1049             :             auto attr(HDF5Attribute::Create(self->GetFullName(),
    1050             :                                             self->GetFullName(), pszObjName,
    1051      462150 :                                             self->m_poShared, hAttr));
    1052      154050 :             if (attr)
    1053             :             {
    1054      154050 :                 self->m_oListAttributes.emplace_back(attr);
    1055             :             }
    1056             :         }
    1057             :     }
    1058      154052 :     return 0;
    1059             : }
    1060             : 
    1061             : /************************************************************************/
    1062             : /*                           GetAttributes()                            */
    1063             : /************************************************************************/
    1064             : 
    1065             : std::vector<std::shared_ptr<GDALAttribute>>
    1066       10919 : HDF5Group::GetAttributes(CSLConstList papszOptions) const
    1067             : {
    1068             :     HDF5_GLOBAL_LOCK();
    1069             : 
    1070       10919 :     m_oListAttributes.clear();
    1071       10919 :     m_bShowAllAttributes =
    1072       10919 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
    1073       10919 :     H5Aiterate(m_hGroup, nullptr, GetAttributesCallback,
    1074             :                const_cast<void *>(static_cast<const void *>(this)));
    1075       10919 :     return m_oListAttributes;
    1076             : }
    1077             : 
    1078             : /************************************************************************/
    1079             : /*                             ~HDF5Array()                             */
    1080             : /************************************************************************/
    1081             : 
    1082        4096 : HDF5Array::~HDF5Array()
    1083             : {
    1084             :     HDF5_GLOBAL_LOCK();
    1085             : 
    1086        2048 :     if (m_hArray > 0)
    1087        2048 :         H5Dclose(m_hArray);
    1088        2048 :     if (m_hNativeDT > 0)
    1089        2048 :         H5Tclose(m_hNativeDT);
    1090        2048 :     if (m_hDataSpace > 0)
    1091        2048 :         H5Sclose(m_hDataSpace);
    1092        4096 : }
    1093             : 
    1094             : /************************************************************************/
    1095             : /*                             HDF5Array()                              */
    1096             : /************************************************************************/
    1097             : 
    1098        2048 : HDF5Array::HDF5Array(const std::string &osParentName, const std::string &osName,
    1099             :                      const std::shared_ptr<HDF5SharedResources> &poShared,
    1100             :                      hid_t hArray, const HDF5Group *poGroup,
    1101        2048 :                      bool bSkipFullDimensionInstantiation)
    1102             :     : GDALAbstractMDArray(osParentName, osName),
    1103             :       GDALMDArray(osParentName, osName), m_osGroupFullname(osParentName),
    1104             :       m_poShared(poShared), m_hArray(hArray),
    1105        2048 :       m_hDataSpace(H5Dget_space(hArray)), m_nOffset(H5Dget_offset(hArray))
    1106             : {
    1107        2048 :     const hid_t hDataType = H5Dget_type(hArray);
    1108        2048 :     m_hNativeDT = H5Tget_native_type(hDataType, H5T_DIR_ASCEND);
    1109        2048 :     H5Tclose(hDataType);
    1110             : 
    1111        2048 :     std::vector<std::pair<std::string, hid_t>> oTypes;
    1112        2048 :     if (!osParentName.empty() && H5Tget_class(m_hNativeDT) == H5T_COMPOUND)
    1113             :     {
    1114         483 :         GetDataTypesInGroup(m_poShared->GetHDF5(), osParentName, oTypes);
    1115             :     }
    1116             : 
    1117        4096 :     m_dt = BuildDataType(m_hNativeDT, m_bHasString, m_bHasNonNativeDataType,
    1118        2048 :                          oTypes);
    1119        2426 :     for (auto &oPair : oTypes)
    1120         378 :         H5Tclose(oPair.second);
    1121             : 
    1122        2763 :     if (m_dt.GetClass() == GEDTC_NUMERIC &&
    1123         715 :         m_dt.GetNumericDataType() == GDT_Unknown)
    1124             :     {
    1125           0 :         CPLDebug("HDF5", "Cannot map data type of %s to a type handled by GDAL",
    1126             :                  osName.c_str());
    1127           0 :         return;
    1128             :     }
    1129             : 
    1130        2048 :     HDF5Array::GetAttributes();
    1131             : 
    1132             :     // Special case for S102 nodata value that is typically at 1e6
    1133        2048 :     if (GetFullName() ==
    1134          38 :             "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values" &&
    1135          76 :         m_dt.GetClass() == GEDTC_COMPOUND &&
    1136          59 :         (m_dt.GetComponents().size() == 1 ||
    1137          21 :          m_dt.GetComponents().size() == 2) &&
    1138          76 :         m_dt.GetSize() == m_dt.GetComponents().size() * sizeof(float) &&
    1139          38 :         m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
    1140        2107 :             GDT_Float32 &&
    1141          59 :         (m_dt.GetComponents().size() == 1 ||
    1142          21 :          m_dt.GetComponents()[1]->GetType().GetNumericDataType() ==
    1143             :              GDT_Float32))
    1144             :     {
    1145          38 :         m_abyNoData.resize(m_dt.GetSize());
    1146          38 :         float afNoData[2] = {1e6f, 1e6f};
    1147             : 
    1148          76 :         if (auto poRootGroup = HDF5Array::GetRootGroup())
    1149             :         {
    1150         114 :             if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
    1151             :             {
    1152             :                 const auto poGroupFArray =
    1153         114 :                     poGroupF->OpenMDArray("BathymetryCoverage");
    1154          38 :                 if (poGroupFArray &&
    1155         114 :                     poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
    1156          76 :                     poGroupFArray->GetDataType().GetComponents().size() == 8 &&
    1157          38 :                     poGroupFArray->GetDataType()
    1158          38 :                             .GetComponents()[0]
    1159          76 :                             ->GetName() == "code" &&
    1160          38 :                     poGroupFArray->GetDataType()
    1161          38 :                             .GetComponents()[3]
    1162          38 :                             ->GetName() == "fillValue" &&
    1163         114 :                     poGroupFArray->GetDimensionCount() == 1 &&
    1164          38 :                     poGroupFArray->GetDimensions()[0]->GetSize() ==
    1165          38 :                         m_dt.GetComponents().size())
    1166             :                 {
    1167             :                     auto poFillValue =
    1168         114 :                         poGroupFArray->GetView("[\"fillValue\"]");
    1169          38 :                     if (poFillValue)
    1170             :                     {
    1171          38 :                         char *pszVal0 = nullptr;
    1172          38 :                         char *pszVal1 = nullptr;
    1173          38 :                         const GUInt64 anArrayStartIdx0[] = {0};
    1174          38 :                         const GUInt64 anArrayStartIdx1[] = {1};
    1175          38 :                         const size_t anCount[] = {1};
    1176          38 :                         const GInt64 anArrayStep[] = {0};
    1177          38 :                         const GPtrDiff_t anBufferStride[] = {0};
    1178          76 :                         poFillValue->Read(anArrayStartIdx0, anCount,
    1179             :                                           anArrayStep, anBufferStride,
    1180          76 :                                           GDALExtendedDataType::CreateString(),
    1181             :                                           &pszVal0);
    1182          38 :                         if (poGroupFArray->GetDimensions()[0]->GetSize() == 2)
    1183             :                         {
    1184          42 :                             poFillValue->Read(
    1185             :                                 anArrayStartIdx1, anCount, anArrayStep,
    1186             :                                 anBufferStride,
    1187          42 :                                 GDALExtendedDataType::CreateString(), &pszVal1);
    1188             :                         }
    1189          38 :                         if (pszVal0)
    1190             :                         {
    1191          38 :                             afNoData[0] = static_cast<float>(CPLAtof(pszVal0));
    1192          38 :                             if (pszVal1)
    1193             :                             {
    1194          21 :                                 afNoData[1] =
    1195          21 :                                     static_cast<float>(CPLAtof(pszVal1));
    1196             :                             }
    1197             :                         }
    1198          38 :                         CPLFree(pszVal0);
    1199          38 :                         CPLFree(pszVal1);
    1200             :                     }
    1201             :                 }
    1202             :             }
    1203             :         }
    1204             : 
    1205          38 :         m_abyNoData.resize(m_dt.GetSize());
    1206          38 :         memcpy(m_abyNoData.data(), afNoData, m_abyNoData.size());
    1207             :     }
    1208             : 
    1209             :     // Special case for S102 QualityOfSurvey nodata value that is typically at 0
    1210             :     const bool bIsQualityOfSurvey =
    1211        2048 :         (GetFullName() ==
    1212             :          "/QualityOfSurvey/QualityOfSurvey.01/Group_001/values");
    1213             :     const bool bIsQualityOfBathymetryCoverage =
    1214        2048 :         (GetFullName() == "/QualityOfBathymetryCoverage/"
    1215             :                           "QualityOfBathymetryCoverage.01/Group_001/values");
    1216        2064 :     if ((bIsQualityOfSurvey || bIsQualityOfBathymetryCoverage) &&
    1217          14 :         ((m_dt.GetClass() == GEDTC_NUMERIC &&
    1218           8 :           m_dt.GetNumericDataType() == GDT_UInt32) ||
    1219           4 :          (m_dt.GetClass() == GEDTC_COMPOUND &&
    1220           4 :           m_dt.GetComponents().size() == 1 &&
    1221           4 :           m_dt.GetComponents()[0]->GetType().GetClass() == GEDTC_NUMERIC &&
    1222           2 :           m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
    1223             :               GDT_UInt32)))
    1224             :     {
    1225          16 :         if (auto poRootGroup = HDF5Array::GetRootGroup())
    1226             :         {
    1227          24 :             if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
    1228             :             {
    1229           8 :                 const auto poGroupFArray = poGroupF->OpenMDArray(
    1230             :                     bIsQualityOfSurvey ? "QualityOfSurvey"
    1231          24 :                                        : "QualityOfBathymetryCoverage");
    1232           8 :                 if (poGroupFArray &&
    1233          24 :                     poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
    1234          16 :                     poGroupFArray->GetDataType().GetComponents().size() == 8 &&
    1235           8 :                     poGroupFArray->GetDataType()
    1236           8 :                             .GetComponents()[0]
    1237          16 :                             ->GetName() == "code" &&
    1238           8 :                     poGroupFArray->GetDataType()
    1239           8 :                             .GetComponents()[3]
    1240           8 :                             ->GetName() == "fillValue" &&
    1241          24 :                     poGroupFArray->GetDimensionCount() == 1 &&
    1242           8 :                     poGroupFArray->GetDimensions()[0]->GetSize() == 1)
    1243             :                 {
    1244             :                     auto poFillValue =
    1245          24 :                         poGroupFArray->GetView("[\"fillValue\"]");
    1246           8 :                     if (poFillValue)
    1247             :                     {
    1248           8 :                         char *pszVal0 = nullptr;
    1249           8 :                         const GUInt64 anArrayStartIdx0[] = {0};
    1250           8 :                         const size_t anCount[] = {1};
    1251           8 :                         const GInt64 anArrayStep[] = {0};
    1252           8 :                         const GPtrDiff_t anBufferStride[] = {0};
    1253          16 :                         poFillValue->Read(anArrayStartIdx0, anCount,
    1254             :                                           anArrayStep, anBufferStride,
    1255          16 :                                           GDALExtendedDataType::CreateString(),
    1256             :                                           &pszVal0);
    1257           8 :                         if (pszVal0)
    1258             :                         {
    1259           8 :                             const uint32_t nNoData = atoi(pszVal0);
    1260           8 :                             m_abyNoData.resize(m_dt.GetSize());
    1261           8 :                             memcpy(m_abyNoData.data(), &nNoData,
    1262             :                                    m_abyNoData.size());
    1263             :                         }
    1264           8 :                         CPLFree(pszVal0);
    1265             :                     }
    1266             :                 }
    1267             :             }
    1268             :         }
    1269             :     }
    1270             : 
    1271             :     // Special case for S104 nodata value that is typically -9999
    1272        2107 :     if (STARTS_WITH(GetFullName().c_str(), "/WaterLevel/WaterLevel.01/") &&
    1273          91 :         GetFullName().find("/values") != std::string::npos &&
    1274          95 :         m_dt.GetClass() == GEDTC_COMPOUND && m_dt.GetSize() == 8 &&
    1275          62 :         m_dt.GetComponents().size() == 2 &&
    1276          31 :         m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
    1277        2107 :             GDT_Float32 &&
    1278             :         // In theory should be Byte, but 104US00_ches_dcf2_20190606T12Z.h5 uses Int32
    1279          31 :         (m_dt.GetComponents()[1]->GetType().GetNumericDataType() == GDT_UInt8 ||
    1280           0 :          m_dt.GetComponents()[1]->GetType().GetNumericDataType() == GDT_Int32))
    1281             :     {
    1282          31 :         m_abyNoData.resize(m_dt.GetSize());
    1283          31 :         float fNoData = -9999.0f;
    1284             : 
    1285          62 :         if (auto poRootGroup = HDF5Array::GetRootGroup())
    1286             :         {
    1287          93 :             if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
    1288             :             {
    1289          93 :                 const auto poGroupFArray = poGroupF->OpenMDArray("WaterLevel");
    1290          31 :                 if (poGroupFArray &&
    1291          93 :                     poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
    1292          62 :                     poGroupFArray->GetDataType().GetComponents().size() == 8 &&
    1293          31 :                     poGroupFArray->GetDataType()
    1294          31 :                             .GetComponents()[0]
    1295          62 :                             ->GetName() == "code" &&
    1296          31 :                     poGroupFArray->GetDataType()
    1297          31 :                             .GetComponents()[3]
    1298          31 :                             ->GetName() == "fillValue" &&
    1299          93 :                     poGroupFArray->GetDimensionCount() == 1 &&
    1300          31 :                     poGroupFArray->GetDimensions()[0]->GetSize() >= 2)
    1301             :                 {
    1302             :                     auto poFillValue =
    1303          93 :                         poGroupFArray->GetView("[\"fillValue\"]");
    1304          31 :                     if (poFillValue)
    1305             :                     {
    1306          31 :                         char *pszVal0 = nullptr;
    1307          31 :                         const GUInt64 anArrayStartIdx0[] = {0};
    1308          31 :                         const size_t anCount[] = {1};
    1309          31 :                         const GInt64 anArrayStep[] = {0};
    1310          31 :                         const GPtrDiff_t anBufferStride[] = {0};
    1311          62 :                         poFillValue->Read(anArrayStartIdx0, anCount,
    1312             :                                           anArrayStep, anBufferStride,
    1313          62 :                                           GDALExtendedDataType::CreateString(),
    1314             :                                           &pszVal0);
    1315          31 :                         if (pszVal0)
    1316             :                         {
    1317          31 :                             fNoData = static_cast<float>(CPLAtof(pszVal0));
    1318             :                         }
    1319          31 :                         CPLFree(pszVal0);
    1320             :                     }
    1321             :                 }
    1322             :             }
    1323             :         }
    1324             : 
    1325          31 :         memcpy(m_abyNoData.data(), &fNoData, sizeof(float));
    1326             :     }
    1327             : 
    1328             :     // Special case for S111 nodata value that is typically -9999
    1329        2048 :     if (STARTS_WITH(GetFullName().c_str(),
    1330          87 :                     "/SurfaceCurrent/SurfaceCurrent.01/") &&
    1331         119 :         GetFullName().find("/values") != std::string::npos &&
    1332          64 :         m_dt.GetClass() == GEDTC_COMPOUND &&
    1333          63 :         m_dt.GetSize() == 2 * sizeof(float) &&
    1334          62 :         m_dt.GetComponents().size() == 2 &&
    1335          31 :         m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
    1336        2135 :             GDT_Float32 &&
    1337          31 :         m_dt.GetComponents()[1]->GetType().GetNumericDataType() == GDT_Float32)
    1338             :     {
    1339          31 :         float afNoData[2] = {-9999.0f, -9999.0f};
    1340             : 
    1341          62 :         if (auto poRootGroup = HDF5Array::GetRootGroup())
    1342             :         {
    1343          93 :             if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
    1344             :             {
    1345             :                 const auto poGroupFArray =
    1346          93 :                     poGroupF->OpenMDArray("SurfaceCurrent");
    1347          31 :                 if (poGroupFArray &&
    1348          93 :                     poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
    1349          62 :                     poGroupFArray->GetDataType().GetComponents().size() == 8 &&
    1350          31 :                     poGroupFArray->GetDataType()
    1351          31 :                             .GetComponents()[0]
    1352          62 :                             ->GetName() == "code" &&
    1353          31 :                     poGroupFArray->GetDataType()
    1354          31 :                             .GetComponents()[3]
    1355          31 :                             ->GetName() == "fillValue" &&
    1356          93 :                     poGroupFArray->GetDimensionCount() == 1 &&
    1357          31 :                     poGroupFArray->GetDimensions()[0]->GetSize() >= 2)
    1358             :                 {
    1359             :                     auto poFillValue =
    1360          93 :                         poGroupFArray->GetView("[\"fillValue\"]");
    1361          31 :                     if (poFillValue)
    1362             :                     {
    1363          31 :                         char *pszVal0 = nullptr;
    1364          31 :                         char *pszVal1 = nullptr;
    1365          31 :                         const GUInt64 anArrayStartIdx0[] = {0};
    1366          31 :                         const GUInt64 anArrayStartIdx1[] = {1};
    1367          31 :                         const size_t anCount[] = {1};
    1368          31 :                         const GInt64 anArrayStep[] = {0};
    1369          31 :                         const GPtrDiff_t anBufferStride[] = {0};
    1370          62 :                         poFillValue->Read(anArrayStartIdx0, anCount,
    1371             :                                           anArrayStep, anBufferStride,
    1372          62 :                                           GDALExtendedDataType::CreateString(),
    1373             :                                           &pszVal0);
    1374          62 :                         poFillValue->Read(anArrayStartIdx1, anCount,
    1375             :                                           anArrayStep, anBufferStride,
    1376          62 :                                           GDALExtendedDataType::CreateString(),
    1377             :                                           &pszVal1);
    1378          31 :                         if (pszVal0 && pszVal1)
    1379             :                         {
    1380          31 :                             afNoData[0] = static_cast<float>(CPLAtof(pszVal0));
    1381          31 :                             afNoData[1] = static_cast<float>(CPLAtof(pszVal1));
    1382             :                         }
    1383          31 :                         CPLFree(pszVal0);
    1384          31 :                         CPLFree(pszVal1);
    1385             :                     }
    1386             :                 }
    1387             :             }
    1388             :         }
    1389             : 
    1390          31 :         m_abyNoData.resize(m_dt.GetSize());
    1391          31 :         memcpy(m_abyNoData.data(), afNoData, m_abyNoData.size());
    1392             :     }
    1393             : 
    1394        2048 :     if (bSkipFullDimensionInstantiation)
    1395             :     {
    1396        1497 :         const int nDims = H5Sget_simple_extent_ndims(m_hDataSpace);
    1397        2994 :         std::vector<hsize_t> anDimSizes(nDims);
    1398        1497 :         if (nDims)
    1399             :         {
    1400        1478 :             H5Sget_simple_extent_dims(m_hDataSpace, &anDimSizes[0], nullptr);
    1401        3805 :             for (int i = 0; i < nDims; ++i)
    1402             :             {
    1403        4654 :                 m_dims.emplace_back(std::make_shared<GDALDimension>(
    1404        4654 :                     std::string(), CPLSPrintf("dim%d", i), std::string(),
    1405        6981 :                     std::string(), anDimSizes[i]));
    1406             :             }
    1407             :         }
    1408             :     }
    1409             :     else
    1410             :     {
    1411         551 :         InstantiateDimensions(osParentName, poGroup);
    1412             :     }
    1413             : }
    1414             : 
    1415             : /************************************************************************/
    1416             : /*                       InstantiateDimensions()                        */
    1417             : /************************************************************************/
    1418             : 
    1419         551 : void HDF5Array::InstantiateDimensions(const std::string &osParentName,
    1420             :                                       const HDF5Group *poGroup)
    1421             : {
    1422         551 :     const int nDims = H5Sget_simple_extent_ndims(m_hDataSpace);
    1423         551 :     std::vector<hsize_t> anDimSizes(nDims);
    1424         551 :     if (nDims)
    1425             :     {
    1426         548 :         H5Sget_simple_extent_dims(m_hDataSpace, &anDimSizes[0], nullptr);
    1427             :     }
    1428             : 
    1429             :     // Build a "classic" subdataset name from group and array names
    1430             :     const std::string osSubdatasetName(
    1431         551 :         "/" +
    1432         551 :         CPLString(osParentName)
    1433        1102 :             .replaceAll("Data Fields", "Data_Fields")
    1434        1653 :             .replaceAll("Geolocation Fields", "Geolocation_Fields") +
    1435        1102 :         "/" + GetName());
    1436             : 
    1437         551 :     if (nDims == 1)
    1438             :     {
    1439         720 :         auto attrCLASS = GetAttribute("CLASS");
    1440         382 :         if (attrCLASS && attrCLASS->GetDimensionCount() == 0 &&
    1441          22 :             attrCLASS->GetDataType().GetClass() == GEDTC_STRING)
    1442             :         {
    1443          22 :             const char *pszStr = attrCLASS->ReadAsString();
    1444          22 :             if (pszStr && EQUAL(pszStr, "DIMENSION_SCALE"))
    1445             :             {
    1446          66 :                 auto attrName = GetAttribute("NAME");
    1447          42 :                 if (attrName &&
    1448          42 :                     attrName->GetDataType().GetClass() == GEDTC_STRING)
    1449             :                 {
    1450          20 :                     const char *pszName = attrName->ReadAsString();
    1451          20 :                     if (pszName &&
    1452          20 :                         STARTS_WITH(pszName, "This is a netCDF dimension but "
    1453             :                                              "not a netCDF variable"))
    1454             :                     {
    1455           0 :                         m_dims.emplace_back(std::make_shared<GDALDimension>(
    1456           0 :                             std::string(), GetName(), std::string(),
    1457           0 :                             std::string(), anDimSizes[0]));
    1458           0 :                         return;
    1459             :                     }
    1460             :                 }
    1461             : 
    1462          44 :                 m_dims.emplace_back(std::make_shared<HDF5Dimension>(
    1463          44 :                     osParentName, GetName(), std::string(), std::string(),
    1464          44 :                     anDimSizes[0], m_poShared));
    1465          22 :                 return;
    1466             :             }
    1467             :         }
    1468             :     }
    1469             : 
    1470         529 :     std::map<size_t, std::string> mapDimIndexToDimFullName;
    1471             : 
    1472         529 :     if (m_bHasDimensionList)
    1473             :     {
    1474          32 :         hid_t hAttr = H5Aopen_name(m_hArray, "DIMENSION_LIST");
    1475          32 :         const hid_t hAttrDataType = H5Aget_type(hAttr);
    1476          32 :         const hid_t hAttrSpace = H5Aget_space(hAttr);
    1477          64 :         if (H5Tget_class(hAttrDataType) == H5T_VLEN &&
    1478          32 :             H5Sget_simple_extent_ndims(hAttrSpace) == 1)
    1479             :         {
    1480          32 :             const hid_t hBaseType = H5Tget_super(hAttrDataType);
    1481          32 :             if (H5Tget_class(hBaseType) == H5T_REFERENCE)
    1482             :             {
    1483          32 :                 hsize_t nSize = 0;
    1484          32 :                 H5Sget_simple_extent_dims(hAttrSpace, &nSize, nullptr);
    1485          32 :                 if (nSize == static_cast<hsize_t>(nDims))
    1486             :                 {
    1487          64 :                     std::vector<hvl_t> aHvl(static_cast<size_t>(nSize));
    1488          32 :                     H5Aread(hAttr, hAttrDataType, &aHvl[0]);
    1489         105 :                     for (size_t i = 0; i < nSize; i++)
    1490             :                     {
    1491         145 :                         if (aHvl[i].len == 1 &&
    1492          72 :                             H5Rget_obj_type(m_hArray, H5R_OBJECT, aHvl[i].p) ==
    1493             :                                 H5G_DATASET)
    1494             :                         {
    1495         144 :                             std::string referenceName;
    1496          72 :                             referenceName.resize(256);
    1497         144 :                             auto ret = H5Rget_name(
    1498          72 :                                 m_poShared->GetHDF5(), H5R_OBJECT, aHvl[i].p,
    1499          72 :                                 &referenceName[0], referenceName.size());
    1500          72 :                             if (ret > 0)
    1501             :                             {
    1502          72 :                                 referenceName.resize(ret);
    1503          72 :                                 mapDimIndexToDimFullName[i] =
    1504         144 :                                     std::move(referenceName);
    1505             :                             }
    1506             :                         }
    1507             :                     }
    1508          32 :                     H5Dvlen_reclaim(hAttrDataType, hAttrSpace, H5P_DEFAULT,
    1509          32 :                                     &aHvl[0]);
    1510             :                 }
    1511             :             }
    1512          32 :             H5Tclose(hBaseType);
    1513             :         }
    1514          32 :         H5Tclose(hAttrDataType);
    1515          32 :         H5Sclose(hAttrSpace);
    1516          32 :         H5Aclose(hAttr);
    1517             :     }
    1518         497 :     else if (m_bHasDimensionLabels)
    1519             :     {
    1520           2 :         hid_t hAttr = H5Aopen_name(m_hArray, "DIMENSION_LABELS");
    1521           2 :         auto attr(HDF5Attribute::Create(m_osGroupFullname, GetFullName(),
    1522           6 :                                         "DIMENSION_LABELS", m_poShared, hAttr));
    1523           4 :         if (attr && attr->GetDimensionCount() == 1 &&
    1524           2 :             attr->GetDataType().GetClass() == GEDTC_STRING)
    1525             :         {
    1526           4 :             auto aosList = attr->ReadAsStringArray();
    1527           2 :             if (aosList.size() == nDims)
    1528             :             {
    1529           8 :                 for (int i = 0; i < nDims; ++i)
    1530             :                 {
    1531           6 :                     if (aosList[i][0] != '\0')
    1532             :                     {
    1533           2 :                         mapDimIndexToDimFullName[i] = aosList[i];
    1534             :                     }
    1535             :                 }
    1536             :             }
    1537             :         }
    1538             :     }
    1539             :     else
    1540             :     {
    1541             :         // Use HDF-EOS5 metadata if available to create dimensions
    1542         495 :         HDF5EOSParser::GridDataFieldMetadata oGridDataFieldMetadata;
    1543         495 :         HDF5EOSParser::SwathFieldMetadata oSwathFieldMetadata;
    1544         495 :         const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
    1545         501 :         if (poHDF5EOSParser &&
    1546           6 :             poHDF5EOSParser->GetGridDataFieldMetadata(osSubdatasetName.c_str(),
    1547         501 :                                                       oGridDataFieldMetadata) &&
    1548           3 :             oGridDataFieldMetadata.aoDimensions.size() ==
    1549           3 :                 static_cast<size_t>(nDims))
    1550             :         {
    1551           6 :             std::map<std::string, std::shared_ptr<GDALDimension>> oMap;
    1552           3 :             const auto groupDims = m_poShared->GetEOSGridDimensions(
    1553           6 :                 oGridDataFieldMetadata.poGridMetadata->osGridName);
    1554          10 :             for (const auto &dim : groupDims)
    1555             :             {
    1556           7 :                 oMap[dim->GetName()] = dim;
    1557             :             }
    1558           3 :             int iDimX = 0;
    1559           3 :             int iDimY = 0;
    1560           3 :             int iCount = 1;
    1561           9 :             for (const auto &oDim : oGridDataFieldMetadata.aoDimensions)
    1562             :             {
    1563           6 :                 auto oIter = oMap.find(oDim.osName);
    1564             :                 // HDF5EOSParser guarantees that
    1565           6 :                 CPLAssert(oIter != oMap.end());
    1566           6 :                 const auto &poDim = oIter->second;
    1567           6 :                 if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
    1568           3 :                     iDimX = iCount;
    1569           3 :                 else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
    1570           3 :                     iDimY = iCount;
    1571           6 :                 ++iCount;
    1572           6 :                 m_dims.emplace_back(poDim);
    1573             :             }
    1574             : 
    1575           3 :             auto poSRS = oGridDataFieldMetadata.poGridMetadata->GetSRS();
    1576           3 :             if (poSRS)
    1577             :             {
    1578           3 :                 m_poSRS = std::shared_ptr<OGRSpatialReference>(poSRS->Clone());
    1579           3 :                 if (iDimX > 0 && iDimY > 0)
    1580             :                 {
    1581           3 :                     if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
    1582           6 :                         std::vector<int>{2, 1})
    1583           1 :                         m_poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
    1584             :                     else
    1585           2 :                         m_poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
    1586             :                 }
    1587             :             }
    1588             : 
    1589           3 :             return;
    1590             :         }
    1591         495 :         else if (poHDF5EOSParser &&
    1592           3 :                  poHDF5EOSParser->GetSwathFieldMetadata(
    1593         495 :                      osSubdatasetName.c_str(), oSwathFieldMetadata) &&
    1594           3 :                  oSwathFieldMetadata.aoDimensions.size() ==
    1595           3 :                      static_cast<size_t>(nDims))
    1596             :         {
    1597           6 :             std::map<std::string, std::shared_ptr<GDALDimension>> oMap;
    1598           3 :             const auto groupDims = m_poShared->GetEOSSwathDimensions(
    1599           3 :                 oSwathFieldMetadata.poSwathMetadata->osSwathName);
    1600          12 :             for (const auto &dim : groupDims)
    1601             :             {
    1602           9 :                 oMap[dim->GetName()] = dim;
    1603             :             }
    1604          10 :             for (const auto &oDim : oSwathFieldMetadata.aoDimensions)
    1605             :             {
    1606           7 :                 auto oIter = oMap.find(oDim.osName);
    1607             :                 // HDF5EOSParser guarantees that
    1608           7 :                 CPLAssert(oIter != oMap.end());
    1609           7 :                 const auto &poDim = oIter->second;
    1610           7 :                 m_dims.emplace_back(poDim);
    1611             :             }
    1612             : 
    1613           3 :             return;
    1614             :         }
    1615             : 
    1616             :         // Special case for S100-family of products (S102, S104, S111)
    1617         104 :         const auto SpecialCaseS100 = [&](const std::string &osCoverageName)
    1618             :         {
    1619         208 :             auto poRootGroup = m_poShared->GetRootGroup();
    1620         104 :             if (poRootGroup)
    1621             :             {
    1622         104 :                 m_poSRS = std::make_shared<OGRSpatialReference>();
    1623         104 :                 if (S100ReadSRS(poRootGroup.get(), *(m_poSRS.get())))
    1624             :                 {
    1625         104 :                     if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
    1626         208 :                         std::vector<int>{2, 1})
    1627          32 :                         m_poSRS->SetDataAxisToSRSAxisMapping({1, 2});
    1628             :                     else
    1629          72 :                         m_poSRS->SetDataAxisToSRSAxisMapping({2, 1});
    1630             :                 }
    1631             :                 else
    1632             :                 {
    1633           0 :                     m_poSRS.reset();
    1634             :                 }
    1635             : 
    1636             :                 auto poCoverage =
    1637         104 :                     poRootGroup->OpenGroupFromFullname(osCoverageName);
    1638         104 :                 if (poCoverage)
    1639             :                 {
    1640         104 :                     std::vector<std::shared_ptr<GDALMDArray>> apoIndexingVars;
    1641         104 :                     if (S100GetDimensions(poCoverage.get(), m_dims,
    1642         104 :                                           apoIndexingVars) &&
    1643         104 :                         m_dims.size() == 2 &&
    1644         312 :                         m_dims[0]->GetSize() == anDimSizes[0] &&
    1645         104 :                         m_dims[1]->GetSize() == anDimSizes[1])
    1646             :                     {
    1647         312 :                         for (const auto &poIndexingVar : apoIndexingVars)
    1648         208 :                             m_poShared->KeepRef(poIndexingVar);
    1649         104 :                         return true;
    1650             :                     }
    1651             :                     else
    1652             :                     {
    1653           0 :                         m_dims.clear();
    1654             :                     }
    1655             :                 }
    1656             :             }
    1657           0 :             return false;
    1658         489 :         };
    1659             : 
    1660         634 :         if (nDims == 2 &&
    1661         145 :             GetFullName() ==
    1662             :                 "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values")
    1663             :         {
    1664             :             // S102
    1665          37 :             if (SpecialCaseS100("/BathymetryCoverage/BathymetryCoverage.01"))
    1666          37 :                 return;
    1667             :         }
    1668         560 :         else if (nDims == 2 &&
    1669         108 :                  GetFullName() ==
    1670             :                      "/QualityOfSurvey/QualityOfSurvey.01/Group_001/values")
    1671             :         {
    1672             :             // S102
    1673           3 :             if (SpecialCaseS100("/QualityOfSurvey/QualityOfSurvey.01"))
    1674           3 :                 return;
    1675             :         }
    1676         554 :         else if (nDims == 2 &&
    1677         105 :                  STARTS_WITH(GetFullName().c_str(),
    1678         554 :                              "/WaterLevel/WaterLevel.01/") &&
    1679          32 :                  GetFullName().find("/values"))
    1680             :         {
    1681             :             // S104
    1682          32 :             if (SpecialCaseS100("/WaterLevel/WaterLevel.01"))
    1683          32 :                 return;
    1684             :         }
    1685         490 :         else if (nDims == 2 &&
    1686          73 :                  STARTS_WITH(GetFullName().c_str(),
    1687         490 :                              "/SurfaceCurrent/SurfaceCurrent.01/") &&
    1688          32 :                  GetFullName().find("/values"))
    1689             :         {
    1690             :             // S111
    1691          32 :             if (SpecialCaseS100("/SurfaceCurrent/SurfaceCurrent.01"))
    1692          32 :                 return;
    1693             :         }
    1694             :     }
    1695             : 
    1696         838 :     std::map<std::string, std::shared_ptr<GDALDimension>> oMapFullNameToDim;
    1697         419 :     bool bMissingDims = true;
    1698             :     // Fill oMapFullNameToDim with dimensions from the current group
    1699         419 :     if (!mapDimIndexToDimFullName.empty())
    1700             :     {
    1701          34 :         CPLAssert(poGroup);
    1702          68 :         const auto groupDims = poGroup->GetDimensions();
    1703         191 :         for (const auto &dim : groupDims)
    1704             :         {
    1705         157 :             oMapFullNameToDim[dim->GetFullName()] = dim;
    1706             :         }
    1707             : 
    1708          34 :         bMissingDims = false;
    1709         106 :         for (int i = 0; i < nDims; ++i)
    1710             :         {
    1711             :             const auto oIter =
    1712          76 :                 mapDimIndexToDimFullName.find(static_cast<size_t>(i));
    1713          76 :             if (oIter != mapDimIndexToDimFullName.end())
    1714             :             {
    1715          71 :                 const auto oIter2 = oMapFullNameToDim.find(oIter->second);
    1716          71 :                 if (oIter2 == oMapFullNameToDim.end())
    1717             :                 {
    1718           4 :                     bMissingDims = true;
    1719           4 :                     break;
    1720             :                 }
    1721             :             }
    1722             :         }
    1723             :     }
    1724             : 
    1725             :     // If not enough, look for dimensions in other groups
    1726         419 :     if (bMissingDims)
    1727             :     {
    1728         778 :         auto poRootGroup = m_poShared->GetRootGroup();
    1729         389 :         if (poRootGroup)
    1730             :         {
    1731         832 :             for (int i = 0; i < nDims; ++i)
    1732             :             {
    1733             :                 auto oIter =
    1734         443 :                     mapDimIndexToDimFullName.find(static_cast<size_t>(i));
    1735         443 :                 if (oIter != mapDimIndexToDimFullName.end())
    1736             :                 {
    1737           7 :                     const auto &osDimFullName = oIter->second;
    1738             :                     auto poDim =
    1739          14 :                         poRootGroup->OpenDimensionFromFullname(osDimFullName);
    1740           7 :                     if (poDim)
    1741             :                     {
    1742           5 :                         oMapFullNameToDim[osDimFullName] = std::move(poDim);
    1743             :                     }
    1744             :                 }
    1745             :             }
    1746             :         }
    1747             :     }
    1748             : 
    1749         929 :     for (int i = 0; i < nDims; ++i)
    1750             :     {
    1751         510 :         auto oIter = mapDimIndexToDimFullName.find(static_cast<size_t>(i));
    1752         510 :         if (oIter != mapDimIndexToDimFullName.end())
    1753             :         {
    1754          74 :             auto oIter2 = oMapFullNameToDim.find(oIter->second);
    1755          74 :             if (oIter2 != oMapFullNameToDim.end())
    1756             :             {
    1757          72 :                 m_dims.emplace_back(oIter2->second);
    1758          72 :                 continue;
    1759             :             }
    1760             : 
    1761           4 :             std::string osDimName(oIter->second);
    1762           2 :             auto nPos = osDimName.rfind('/');
    1763           2 :             if (nPos != std::string::npos)
    1764             :             {
    1765           0 :                 const std::string osDimParentName(osDimName.substr(0, nPos));
    1766           0 :                 osDimName = osDimName.substr(nPos + 1);
    1767             : 
    1768           0 :                 m_dims.emplace_back(std::make_shared<HDF5Dimension>(
    1769           0 :                     osDimParentName.empty() ? "/" : osDimParentName, osDimName,
    1770           0 :                     std::string(), std::string(), anDimSizes[i], m_poShared));
    1771             :             }
    1772             :             else
    1773             :             {
    1774           4 :                 m_dims.emplace_back(std::make_shared<GDALDimension>(
    1775           4 :                     std::string(), osDimName, std::string(), std::string(),
    1776           4 :                     anDimSizes[i]));
    1777             :             }
    1778             :         }
    1779             :         else
    1780             :         {
    1781         872 :             m_dims.emplace_back(std::make_shared<GDALDimension>(
    1782         872 :                 std::string(), CPLSPrintf("dim%d", i), std::string(),
    1783        1308 :                 std::string(), anDimSizes[i]));
    1784             :         }
    1785             :     }
    1786             : 
    1787             :     // Use HDF-EOS5 metadata if available to create SRS
    1788         838 :     HDF5EOSParser::GridDataFieldMetadata oGridDataFieldMetadata;
    1789         419 :     const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
    1790         420 :     if (poHDF5EOSParser &&
    1791           1 :         poHDF5EOSParser->GetGridDataFieldMetadata(osSubdatasetName.c_str(),
    1792             :                                                   oGridDataFieldMetadata))
    1793             :     {
    1794           2 :         auto poSRS = oGridDataFieldMetadata.poGridMetadata->GetSRS();
    1795           1 :         if (poSRS)
    1796             :         {
    1797           1 :             int iDimX = 0;
    1798           1 :             int iDimY = 0;
    1799           4 :             for (int iDim = 0; iDim < static_cast<int>(m_dims.size()); ++iDim)
    1800             :             {
    1801           3 :                 const auto &poDim = m_dims[iDim];
    1802           3 :                 if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
    1803           1 :                     iDimX = iDim + 1;
    1804           2 :                 else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
    1805           1 :                     iDimY = iDim + 1;
    1806             :             }
    1807             : 
    1808           1 :             if (iDimX > 0 && iDimY > 0)
    1809             :             {
    1810           1 :                 m_poSRS = std::shared_ptr<OGRSpatialReference>(poSRS->Clone());
    1811           1 :                 if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
    1812           2 :                     std::vector<int>{2, 1})
    1813           0 :                     m_poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
    1814             :                 else
    1815           1 :                     m_poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
    1816             :             }
    1817             :         }
    1818             :     }
    1819             : }
    1820             : 
    1821             : /************************************************************************/
    1822             : /*                       GetCoordinateVariables()                       */
    1823             : /************************************************************************/
    1824             : 
    1825             : std::vector<std::shared_ptr<GDALMDArray>>
    1826           3 : HDF5Array::GetCoordinateVariables() const
    1827             : {
    1828           3 :     std::vector<std::shared_ptr<GDALMDArray>> ret;
    1829             : 
    1830           6 :     HDF5EOSParser::SwathFieldMetadata oSwathFieldMetadata;
    1831           3 :     const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
    1832             :     // Build a "classic" subdataset name from group and array names
    1833             :     const std::string osSubdatasetName(
    1834           3 :         "/" + CPLString(GetFullName())
    1835           6 :                   .replaceAll("Data Fields", "Data_Fields")
    1836           9 :                   .replaceAll("Geolocation Fields", "Geolocation_Fields"));
    1837           6 :     if (poHDF5EOSParser &&
    1838           3 :         poHDF5EOSParser->GetSwathFieldMetadata(osSubdatasetName.c_str(),
    1839           6 :                                                oSwathFieldMetadata) &&
    1840           1 :         oSwathFieldMetadata.aoDimensions.size() == GetDimensionCount())
    1841             :     {
    1842           1 :         if (!oSwathFieldMetadata.osLongitudeSubdataset.empty() &&
    1843           1 :             oSwathFieldMetadata.nPixelOffset == 0 &&
    1844           1 :             oSwathFieldMetadata.nLineOffset == 0 &&
    1845           3 :             oSwathFieldMetadata.nPixelStep == 1 &&
    1846           1 :             oSwathFieldMetadata.nLineStep == 1)
    1847             :         {
    1848           2 :             auto poRootGroup = m_poShared->GetRootGroup();
    1849           1 :             if (poRootGroup)
    1850             :             {
    1851           1 :                 auto poLongitude = poRootGroup->OpenMDArrayFromFullname(
    1852           2 :                     CPLString(
    1853           0 :                         oSwathFieldMetadata.osLongitudeSubdataset.substr(1))
    1854             :                         .replaceAll("Geolocation_Fields",
    1855           4 :                                     "Geolocation Fields"));
    1856           1 :                 auto poLatitude = poRootGroup->OpenMDArrayFromFullname(
    1857           2 :                     CPLString(
    1858           0 :                         oSwathFieldMetadata.osLatitudeSubdataset.substr(1))
    1859             :                         .replaceAll("Geolocation_Fields",
    1860           4 :                                     "Geolocation Fields"));
    1861           1 :                 if (poLongitude && poLatitude)
    1862             :                 {
    1863           1 :                     ret.push_back(std::move(poLongitude));
    1864           1 :                     ret.push_back(std::move(poLatitude));
    1865             :                 }
    1866             :             }
    1867             :         }
    1868             :     }
    1869             : 
    1870           6 :     return ret;
    1871             : }
    1872             : 
    1873             : /************************************************************************/
    1874             : /*                           GetFilterInfo()                            */
    1875             : /************************************************************************/
    1876             : 
    1877           8 : static CPLStringList GetFilterInfo(hid_t hArray, unsigned nFilterMask)
    1878             : {
    1879             :     static const struct
    1880             :     {
    1881             :         int nFilterId;
    1882             :         const char *pszName;
    1883             :     } gasHDF5Filters[] = {
    1884             :         // { H5Z_FILTER_DEFLATE, "DEFLATE" },
    1885             :         {H5Z_FILTER_SHUFFLE, "SHUFFLE"},
    1886             :         {H5Z_FILTER_FLETCHER32, "FLETCHER32"},
    1887             :         // { H5Z_FILTER_SZIP, "SZIP" },
    1888             :         {H5Z_FILTER_NBIT, "NBIT"},
    1889             :         {H5Z_FILTER_SCALEOFFSET, "SCALEOFFSET"},
    1890             :         // Below from netcdf_filter.h
    1891             :         {/*H5Z_FILTER_ZSTD */ 32015, "ZSTD"},
    1892             :         {/*H5Z_FILTER_BZIP2 */ 307, "BZIP2"},
    1893             :         {/*H5Z_FILTER_BLOSC */ 32001, "BLOSC"},
    1894             :     };
    1895             : 
    1896           8 :     CPLStringList aosInfo;
    1897           8 :     const hid_t nListId = H5Dget_create_plist(hArray);
    1898           8 :     if (nListId > 0)
    1899             :     {
    1900           8 :         const int nFilters = H5Pget_nfilters(nListId);
    1901           8 :         const char *pszCompression = nullptr;
    1902          16 :         std::string osFilters;
    1903          12 :         for (int i = 0; i < nFilters; ++i)
    1904             :         {
    1905           4 :             unsigned int flags = 0;
    1906           4 :             size_t cd_nelmts = 0;
    1907           4 :             char szName[64 + 1] = {0};
    1908             :             const auto eFilter =
    1909           4 :                 H5Pget_filter(nListId, i, &flags, &cd_nelmts, nullptr,
    1910             :                               sizeof(szName) - 1, szName);
    1911           4 :             if ((flags & nFilterMask) == 0)
    1912             :             {
    1913           4 :                 if (eFilter == H5Z_FILTER_DEFLATE)
    1914             :                 {
    1915           2 :                     pszCompression = "DEFLATE";
    1916             :                 }
    1917           2 :                 else if (eFilter == H5Z_FILTER_SZIP)
    1918             :                 {
    1919           0 :                     pszCompression = "SZIP";
    1920             :                 }
    1921             :                 else
    1922             :                 {
    1923           2 :                     bool bFound = false;
    1924           2 :                     if (!osFilters.empty())
    1925           0 :                         osFilters += ',';
    1926           2 :                     for (const auto &sFilterInfo : gasHDF5Filters)
    1927             :                     {
    1928           2 :                         if (sFilterInfo.nFilterId == eFilter)
    1929             :                         {
    1930           2 :                             bFound = true;
    1931           2 :                             osFilters += sFilterInfo.pszName;
    1932           2 :                             break;
    1933             :                         }
    1934             :                     }
    1935           2 :                     if (!bFound)
    1936           0 :                         osFilters += szName;
    1937             :                 }
    1938             :             }
    1939             :         }
    1940           8 :         H5Pclose(nListId);
    1941           8 :         if (pszCompression)
    1942           2 :             aosInfo.SetNameValue(GDALMD_COMPRESSION, pszCompression);
    1943           8 :         if (!osFilters.empty())
    1944           2 :             aosInfo.SetNameValue("FILTER", osFilters.c_str());
    1945             :     }
    1946           8 :     return aosInfo;
    1947             : }
    1948             : 
    1949             : /************************************************************************/
    1950             : /*                     HDF5Array::GetRawBlockInfo()                     */
    1951             : /************************************************************************/
    1952             : 
    1953           4 : bool HDF5Array::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
    1954             :                                 GDALMDArrayRawBlockInfo &info) const
    1955             : {
    1956           4 :     info.clear();
    1957             : 
    1958           8 :     const auto anBlockSize = GetBlockSize();
    1959           4 :     CPLAssert(anBlockSize.size() == m_dims.size());
    1960             : 
    1961           7 :     const auto AddExtraInfo = [this, &info]()
    1962             :     {
    1963           3 :         if (m_dt.GetSize() > 1)
    1964             :         {
    1965           2 :             const hid_t hDataType = H5Dget_type(m_hArray);
    1966           2 :             switch (H5Tget_order(hDataType))
    1967             :             {
    1968           1 :                 case H5T_ORDER_LE:
    1969           1 :                     info.papszInfo =
    1970           1 :                         CSLSetNameValue(info.papszInfo, "ENDIANNESS", "LITTLE");
    1971           1 :                     break;
    1972           1 :                 case H5T_ORDER_BE:
    1973           1 :                     info.papszInfo =
    1974           1 :                         CSLSetNameValue(info.papszInfo, "ENDIANNESS", "BIG");
    1975           1 :                     break;
    1976           0 :                 case H5T_ORDER_VAX:
    1977           0 :                     info.papszInfo =
    1978           0 :                         CSLSetNameValue(info.papszInfo, "ENDIANNESS", "VAX");
    1979           0 :                     break;
    1980           0 :                 case H5T_ORDER_MIXED:
    1981           0 :                     info.papszInfo =
    1982           0 :                         CSLSetNameValue(info.papszInfo, "ENDIANNESS", "MIXED");
    1983           0 :                     break;
    1984           0 :                 case H5T_ORDER_NONE:
    1985             :                 case H5T_ORDER_ERROR:
    1986           0 :                     break;
    1987             :             }
    1988           2 :             H5Tclose(hDataType);
    1989             :         }
    1990           7 :     };
    1991             : 
    1992           4 :     if (!anBlockSize.empty() && anBlockSize[0] == 0)
    1993             :     {
    1994             :         HDF5_GLOBAL_LOCK();
    1995           4 :         const auto nOffset = H5Dget_offset(m_hArray);
    1996           4 :         if (nOffset != HADDR_UNDEF)
    1997             :         {
    1998           4 :             bool bAllZeroes = true;
    1999          12 :             for (size_t i = 0; i < m_dims.size() && bAllZeroes; ++i)
    2000           8 :                 bAllZeroes = panBlockCoordinates[i] == 0;
    2001           4 :             if (bAllZeroes)
    2002             :             {
    2003           3 :                 info.pszFilename = CPLStrdup(m_poShared->GetFilename().c_str());
    2004           3 :                 info.nOffset = nOffset;
    2005           3 :                 info.nSize = H5Dget_storage_size(m_hArray);
    2006           3 :                 info.papszInfo =
    2007           3 :                     GetFilterInfo(m_hArray, /* nFilterMask = */ 0).StealList();
    2008           3 :                 AddExtraInfo();
    2009           3 :                 return true;
    2010             :             }
    2011             :             else
    2012             :             {
    2013           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2014             :                          "GetRawBlockInfo() failed: invalid block coordinates. "
    2015             :                          "Should be all 0");
    2016           1 :                 return false;
    2017             :             }
    2018             :         }
    2019             :         else
    2020             :         {
    2021           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2022             :                      "GetRawBlockInfo() failed: array %s is not chunked or "
    2023             :                      "contiguous",
    2024           0 :                      GetName().c_str());
    2025           0 :             return false;
    2026             :         }
    2027             :     }
    2028             : 
    2029           0 :     [[maybe_unused]] uint64_t nAdditionalOffset = 0;
    2030             : #if !(defined(H5_VERS_MAJOR) &&                                                \
    2031             :       (H5_VERS_MAJOR >= 2 ||                                                   \
    2032             :        (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 14 && H5_VERS_RELEASE >= 4)))
    2033             : #ifdef HAVE_H5Dchunk_iter
    2034             :     if (m_asBlockInfo.empty())
    2035             : #endif
    2036             :     {
    2037             :         // Before HDF5 1.14.4, H5Dget_chunk_info() doesn't take into account the
    2038             :         // user block
    2039           0 :         const hid_t nListId = H5Fget_create_plist(m_poShared->GetHDF5());
    2040           0 :         if (nListId > 0)
    2041             :         {
    2042           0 :             hsize_t nUserBlockSize = 0;
    2043           0 :             if (H5Pget_userblock(nListId, &nUserBlockSize) >= 0)
    2044             :             {
    2045           0 :                 nAdditionalOffset = nUserBlockSize;
    2046             :             }
    2047           0 :             H5Pclose(nListId);
    2048             :         }
    2049             :     }
    2050             : #endif
    2051             : 
    2052             : #if (defined(H5_VERS_MAJOR) &&                                                 \
    2053             :      (H5_VERS_MAJOR >= 2 || (H5_VERS_MAJOR == 1 && H5_VERS_MINOR > 10) ||      \
    2054             :       (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 10 && H5_VERS_RELEASE >= 5)))
    2055             :     // Compute the block index from the coordinates
    2056             :     hsize_t nBlockIdx = 0;
    2057             :     hsize_t nMult = 1;
    2058             :     std::vector<uint64_t> anBlockCount(anBlockSize.size());
    2059             :     for (size_t nDimIdx = anBlockSize.size(); nDimIdx > 0;)
    2060             :     {
    2061             :         --nDimIdx;
    2062             :         const auto nBlockSize = anBlockSize[nDimIdx];
    2063             :         const auto nBlockCount =
    2064             :             cpl::div_round_up(m_dims[nDimIdx]->GetSize(), nBlockSize);
    2065             :         if (panBlockCoordinates[nDimIdx] >= nBlockCount)
    2066             :         {
    2067             :             CPLError(CE_Failure, CPLE_AppDefined,
    2068             :                      "GetRawBlockInfo() failed: array %s: "
    2069             :                      "invalid block coordinate (%u) for dimension %u",
    2070             :                      GetName().c_str(),
    2071             :                      static_cast<unsigned>(panBlockCoordinates[nDimIdx]),
    2072             :                      static_cast<unsigned>(nDimIdx));
    2073             :             return false;
    2074             :         }
    2075             :         anBlockCount[nDimIdx] = nBlockCount;
    2076             :         nBlockIdx += panBlockCoordinates[nDimIdx] * nMult;
    2077             :         nMult *= nBlockCount;
    2078             :     }
    2079             : 
    2080             :     HDF5_GLOBAL_LOCK();
    2081             : 
    2082             : #ifdef HAVE_H5Dchunk_iter
    2083             :     if (nBlockIdx == 0 && m_asBlockInfo.empty() && m_bFirstBlockInfo)
    2084             :     {
    2085             :         // If asking block 0, we assume that all blocks will be iterated
    2086             :         // and thus use H5Dchunk_iter() instead of H5Dget_chunk_info() for
    2087             :         // faster execution.
    2088             : 
    2089             :         m_bFirstBlockInfo = false;
    2090             :         const hsize_t nTotalBlockCount = nMult;
    2091             :         bool bTooManyBlocks = false;
    2092             : 
    2093             :         if constexpr (sizeof(size_t) < sizeof(hsize_t))
    2094             :         {
    2095             :             if (nTotalBlockCount > std::numeric_limits<size_t>::max())
    2096             :             {
    2097             :                 bTooManyBlocks = true;
    2098             :             }
    2099             :         }
    2100             : 
    2101             :         if (!bTooManyBlocks)
    2102             :         {
    2103             :             try
    2104             :             {
    2105             :                 m_asBlockInfo.resize(static_cast<size_t>(nTotalBlockCount));
    2106             :             }
    2107             :             catch (const std::exception &)
    2108             :             {
    2109             :                 bTooManyBlocks = true;
    2110             :             }
    2111             :         }
    2112             : 
    2113             :         if (bTooManyBlocks)
    2114             :         {
    2115             :             CPLDebugOnce("HDF5",
    2116             :                          "Too many blocks w.r.t. RAM to use H5Dchunk_iter(). "
    2117             :                          "Falling back to slower method");
    2118             :         }
    2119             :         else
    2120             :         {
    2121             :             struct FillStruct
    2122             :             {
    2123             :                 std::vector<BlockInfo> *pasBlockInfo = nullptr;
    2124             :                 std::vector<GUInt64> anBlockSize{};
    2125             :                 std::vector<uint64_t> anBlockCount{};
    2126             :                 uint64_t nAdditionalOffset = 0;
    2127             : 
    2128             :                 static int callback(const hsize_t *chunkCoords,
    2129             :                                     unsigned filter_mask, haddr_t addr,
    2130             :                                     hsize_t size, void *op_data)
    2131             :                 {
    2132             :                     FillStruct *fillStruct = static_cast<FillStruct *>(op_data);
    2133             :                     uint64_t nIdx64 = 0;
    2134             :                     uint64_t nMult = 1;
    2135             :                     for (size_t iDim = fillStruct->anBlockCount.size();
    2136             :                          iDim > 0;
    2137             :                          /* */)
    2138             :                     {
    2139             :                         --iDim;
    2140             :                         const auto nBlockCoord =
    2141             :                             chunkCoords[iDim] / fillStruct->anBlockSize[iDim];
    2142             :                         CPLAssert(nBlockCoord < fillStruct->anBlockCount[iDim]);
    2143             :                         nIdx64 += nBlockCoord * nMult;
    2144             :                         nMult *= fillStruct->anBlockCount[iDim];
    2145             :                     }
    2146             :                     CPLAssert(nIdx64 <= fillStruct->pasBlockInfo->size());
    2147             :                     auto &sBlockInfo =
    2148             :                         (*fillStruct
    2149             :                               ->pasBlockInfo)[static_cast<size_t>(nIdx64)];
    2150             :                     sBlockInfo.nOffset =
    2151             :                         addr == HADDR_UNDEF
    2152             :                             ? 0
    2153             :                             : addr + fillStruct->nAdditionalOffset;
    2154             :                     sBlockInfo.nSize = size;
    2155             :                     sBlockInfo.nFilterMask = filter_mask;
    2156             : #ifdef DEBUG_VERBOSE
    2157             :                     CPLDebug("HDF5",
    2158             :                              "Block %" PRIu64 ": offset = %" PRIu64
    2159             :                              ", size = %" PRIu64,
    2160             :                              nIdx64, sBlockInfo.nOffset, sBlockInfo.nSize);
    2161             : #endif
    2162             :                     return 0;
    2163             :                 }
    2164             :             };
    2165             : 
    2166             :             FillStruct fillStruct;
    2167             :             fillStruct.pasBlockInfo = &m_asBlockInfo;
    2168             :             fillStruct.nAdditionalOffset = nAdditionalOffset;
    2169             :             fillStruct.anBlockSize = anBlockSize;
    2170             :             fillStruct.anBlockCount = anBlockCount;
    2171             : 
    2172             :             if (H5Dchunk_iter(m_hArray, H5P_DEFAULT, &FillStruct::callback,
    2173             :                               &fillStruct) < 0)
    2174             :             {
    2175             :                 m_asBlockInfo.clear();
    2176             :                 CPLError(CE_Failure, CPLE_AppDefined,
    2177             :                          "GetRawBlockInfo() failed: array %s: H5Dchunk_iter() "
    2178             :                          "failed",
    2179             :                          GetName().c_str());
    2180             :                 return false;
    2181             :             }
    2182             :         }
    2183             :     }
    2184             : 
    2185             :     if (!m_asBlockInfo.empty())
    2186             :     {
    2187             :         CPLAssert(nBlockIdx < m_asBlockInfo.size());
    2188             :         const size_t nIdx = static_cast<size_t>(nBlockIdx);
    2189             :         info.pszFilename = CPLStrdup(m_poShared->GetFilename().c_str());
    2190             :         info.nOffset = m_asBlockInfo[nIdx].nOffset;
    2191             :         info.nSize = m_asBlockInfo[nIdx].nSize;
    2192             :         info.papszInfo =
    2193             :             GetFilterInfo(m_hArray, m_asBlockInfo[nIdx].nFilterMask)
    2194             :                 .StealList();
    2195             :         AddExtraInfo();
    2196             :         return true;
    2197             :     }
    2198             : #endif
    2199             : 
    2200             :     hsize_t nActualChunks = 0;
    2201             :     if (H5Dget_num_chunks(m_hArray, m_hDataSpace, &nActualChunks) < 0)
    2202             :     {
    2203             :         CPLError(
    2204             :             CE_Failure, CPLE_AppDefined,
    2205             :             "GetRawBlockInfo() failed: array %s: H5Dget_num_chunks() failed",
    2206             :             GetName().c_str());
    2207             :         return false;
    2208             :     }
    2209             :     const auto nExpectedChunks =
    2210             :         std::accumulate(anBlockCount.begin(), anBlockCount.end(),
    2211             :                         std::uint64_t{1}, std::multiplies<>());
    2212             :     const bool bSparse = nExpectedChunks != nActualChunks;
    2213             : 
    2214             :     unsigned nFilterMask = 0;
    2215             :     haddr_t nChunkOffset = 0;
    2216             :     hsize_t nChunkSize = 0;
    2217             : 
    2218             :     if (bSparse)
    2219             :     {
    2220             :         std::vector<hsize_t> anCoordOffsets(GetDimensionCount());
    2221             :         for (size_t i = 0; i < GetDimensionCount(); i++)
    2222             :         {
    2223             :             anCoordOffsets[i] = panBlockCoordinates[i] * anBlockSize[i];
    2224             :         }
    2225             :         if (H5Dget_chunk_info_by_coord(m_hArray, anCoordOffsets.data(),
    2226             :                                        &nFilterMask, &nChunkOffset,
    2227             :                                        &nChunkSize) < 0)
    2228             :         {
    2229             :             CPLError(CE_Failure, CPLE_AppDefined,
    2230             :                      "GetRawBlockInfo() failed: array %s: "
    2231             :                      "H5Dget_chunk_info_by_coord() failed",
    2232             :                      GetName().c_str());
    2233             :             return false;
    2234             :         }
    2235             :     }
    2236             :     else
    2237             :     {
    2238             :         std::vector<hsize_t> anOffset(GetDimensionCount());
    2239             :         if (H5Dget_chunk_info(m_hArray, m_hDataSpace, nBlockIdx,
    2240             :                               anOffset.data(), &nFilterMask, &nChunkOffset,
    2241             :                               &nChunkSize) < 0)
    2242             :         {
    2243             :             CPLError(CE_Failure, CPLE_AppDefined,
    2244             :                      "GetRawBlockInfo() failed: array %s: H5Dget_chunk_info() "
    2245             :                      "failed",
    2246             :                      GetName().c_str());
    2247             :             return false;
    2248             :         }
    2249             :     }
    2250             : 
    2251             :     info.pszFilename = CPLStrdup(m_poShared->GetFilename().c_str());
    2252             :     info.nOffset =
    2253             :         nChunkOffset == HADDR_UNDEF ? 0 : nChunkOffset + nAdditionalOffset;
    2254             :     info.nSize = nChunkSize;
    2255             :     info.papszInfo = GetFilterInfo(m_hArray, nFilterMask).StealList();
    2256             :     AddExtraInfo();
    2257             : 
    2258             :     return true;
    2259             : #else
    2260           0 :     CPLDebug("HDF5", "H5Dget_chunk_info() only available in HDF5 >= 1.10.5");
    2261           0 :     return false;
    2262             : #endif
    2263             : }
    2264             : 
    2265             : /************************************************************************/
    2266             : /*                       GetAttributesCallback()                        */
    2267             : /************************************************************************/
    2268             : 
    2269        6076 : herr_t HDF5Array::GetAttributesCallback(hid_t hArray, const char *pszObjName,
    2270             :                                         void *selfIn)
    2271             : {
    2272        6076 :     HDF5Array *self = static_cast<HDF5Array *>(selfIn);
    2273        6076 :     if (self->m_bShowAllAttributes ||
    2274        3519 :         (strcmp(pszObjName, "_Netcdf4Dimid") != 0 &&
    2275        3103 :          strcmp(pszObjName, "_Netcdf4Coordinates") != 0 &&
    2276        3091 :          strcmp(pszObjName, "CLASS") != 0 && strcmp(pszObjName, "NAME") != 0))
    2277             :     {
    2278        5368 :         if (EQUAL(pszObjName, "DIMENSION_LIST"))
    2279             :         {
    2280         519 :             self->m_bHasDimensionList = true;
    2281         519 :             if (!self->m_bShowAllAttributes)
    2282         353 :                 return 0;
    2283             :         }
    2284        5015 :         if (EQUAL(pszObjName, "DIMENSION_LABELS"))
    2285             :         {
    2286           9 :             self->m_bHasDimensionLabels = true;
    2287           9 :             if (!self->m_bShowAllAttributes)
    2288           6 :                 return 0;
    2289             :         }
    2290             : 
    2291        5009 :         hid_t hAttr = H5Aopen_name(hArray, pszObjName);
    2292        5009 :         if (hAttr > 0)
    2293             :         {
    2294        5009 :             auto attr(HDF5Attribute::Create(self->m_osGroupFullname,
    2295             :                                             self->GetFullName(), pszObjName,
    2296       10018 :                                             self->m_poShared, hAttr));
    2297        5009 :             if (attr)
    2298             :             {
    2299             :                 // Used by HDF5-EOS products
    2300          58 :                 if (EQUAL(pszObjName, "_FillValue") &&
    2301         116 :                     self->GetDataType().GetClass() == GEDTC_NUMERIC &&
    2302        4683 :                     attr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    2303          58 :                     attr->GetDimensionCount() == 0)
    2304             :                 {
    2305          58 :                     auto oRawResult(attr->ReadAsRaw());
    2306          58 :                     if (oRawResult.data())
    2307             :                     {
    2308             :                         // Round-trip attribute value to target data type and back
    2309             :                         // to attribute data type to ensure there is no loss
    2310             :                         // Normally _FillValue data type should be the same
    2311             :                         // as the array one, but this is not always the case.
    2312             :                         // For example NASA GEDI L2B products have Float64
    2313             :                         // _FillValue for Float32 variables.
    2314          58 :                         self->m_abyNoData.resize(self->GetDataType().GetSize());
    2315         174 :                         GDALExtendedDataType::CopyValue(
    2316          58 :                             oRawResult.data(), attr->GetDataType(),
    2317          58 :                             self->m_abyNoData.data(), self->GetDataType());
    2318             :                         std::vector<GByte> abyTmp(
    2319          58 :                             attr->GetDataType().GetSize());
    2320         174 :                         GDALExtendedDataType::CopyValue(
    2321          58 :                             self->m_abyNoData.data(), self->GetDataType(),
    2322          58 :                             abyTmp.data(), attr->GetDataType());
    2323          58 :                         std::vector<GByte> abyOri;
    2324          58 :                         abyOri.assign(oRawResult.data(),
    2325          58 :                                       oRawResult.data() + oRawResult.size());
    2326          58 :                         if (abyOri == abyTmp)
    2327             :                         {
    2328          54 :                             if (!self->m_bShowAllAttributes)
    2329          38 :                                 return 0;
    2330             :                         }
    2331             :                         else
    2332             :                         {
    2333           4 :                             self->m_abyNoData.clear();
    2334           4 :                             if (!self->m_bWarnedNoData)
    2335             :                             {
    2336           2 :                                 self->m_bWarnedNoData = true;
    2337           2 :                                 char *pszVal = nullptr;
    2338           4 :                                 GDALExtendedDataType::CopyValue(
    2339           2 :                                     oRawResult.data(), attr->GetDataType(),
    2340             :                                     &pszVal,
    2341           4 :                                     GDALExtendedDataType::CreateString());
    2342           6 :                                 CPLError(CE_Warning, CPLE_AppDefined,
    2343             :                                          "Array %s: %s attribute value (%s) is "
    2344             :                                          "not in "
    2345             :                                          "the range of the "
    2346             :                                          "array data type (%s)",
    2347           2 :                                          self->GetName().c_str(), pszObjName,
    2348           2 :                                          pszVal ? pszVal : "(null)",
    2349             :                                          GDALGetDataTypeName(
    2350           2 :                                              self->GetDataType()
    2351             :                                                  .GetNumericDataType()));
    2352           2 :                                 CPLFree(pszVal);
    2353             :                             }
    2354             :                         }
    2355             :                     }
    2356             :                 }
    2357             : 
    2358          78 :                 if (EQUAL(pszObjName, "units") &&
    2359        4607 :                     attr->GetDataType().GetClass() == GEDTC_STRING &&
    2360          78 :                     attr->GetDimensionCount() == 0)
    2361             :                 {
    2362          78 :                     const char *pszStr = attr->ReadAsString();
    2363          78 :                     if (pszStr)
    2364             :                     {
    2365          78 :                         self->m_osUnit = pszStr;
    2366          78 :                         if (!self->m_bShowAllAttributes)
    2367          39 :                             return 0;
    2368             :                     }
    2369             :                 }
    2370             : 
    2371        4490 :                 self->m_oListAttributes.emplace_back(attr);
    2372             :             }
    2373             :         }
    2374             :     }
    2375        5640 :     return 0;
    2376             : }
    2377             : 
    2378             : /************************************************************************/
    2379             : /*                     GetAttributeFromAttributes()                     */
    2380             : /************************************************************************/
    2381             : 
    2382             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
    2383             :  */
    2384             : std::shared_ptr<GDALAttribute>
    2385        1820 : HDF5Array::GetAttribute(const std::string &osName) const
    2386             : {
    2387        1820 :     const char *const apszOptions[] = {"SHOW_ALL=YES", nullptr};
    2388        1820 :     if (!m_bShowAllAttributes)
    2389        1697 :         GetAttributes(apszOptions);
    2390        3760 :     for (const auto &attr : m_oListAttributes)
    2391             :     {
    2392        2186 :         if (attr->GetName() == osName)
    2393         246 :             return attr;
    2394             :     }
    2395        1574 :     return nullptr;
    2396             : }
    2397             : 
    2398             : /************************************************************************/
    2399             : /*                           GetAttributes()                            */
    2400             : /************************************************************************/
    2401             : 
    2402             : std::vector<std::shared_ptr<GDALAttribute>>
    2403        3759 : HDF5Array::GetAttributes(CSLConstList papszOptions) const
    2404             : {
    2405             :     HDF5_GLOBAL_LOCK();
    2406             : 
    2407        3759 :     m_oListAttributes.clear();
    2408        3759 :     m_bShowAllAttributes =
    2409        3759 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
    2410        3759 :     H5Aiterate(m_hArray, nullptr, GetAttributesCallback,
    2411             :                const_cast<void *>(static_cast<const void *>(this)));
    2412        3759 :     return m_oListAttributes;
    2413             : }
    2414             : 
    2415             : /************************************************************************/
    2416             : /*                            GetBlockSize()                            */
    2417             : /************************************************************************/
    2418             : 
    2419         234 : std::vector<GUInt64> HDF5Array::GetBlockSize() const
    2420             : {
    2421             :     HDF5_GLOBAL_LOCK();
    2422             : 
    2423         234 :     const auto nDimCount = GetDimensionCount();
    2424         234 :     std::vector<GUInt64> res(nDimCount);
    2425         234 :     if (res.empty())
    2426           1 :         return res;
    2427             : 
    2428         233 :     const hid_t nListId = H5Dget_create_plist(m_hArray);
    2429         233 :     if (nListId > 0)
    2430             :     {
    2431         233 :         if (H5Pget_layout(nListId) == H5D_CHUNKED)
    2432             :         {
    2433         318 :             std::vector<hsize_t> anChunkDims(nDimCount);
    2434         159 :             const int nDimSize = H5Pget_chunk(
    2435         159 :                 nListId, static_cast<int>(nDimCount), &anChunkDims[0]);
    2436         159 :             if (static_cast<size_t>(nDimSize) == nDimCount)
    2437             :             {
    2438         477 :                 for (size_t i = 0; i < nDimCount; ++i)
    2439             :                 {
    2440         318 :                     res[i] = anChunkDims[i];
    2441             :                 }
    2442             :             }
    2443             :         }
    2444             : 
    2445         233 :         H5Pclose(nListId);
    2446             :     }
    2447             : 
    2448         233 :     return res;
    2449             : }
    2450             : 
    2451             : /************************************************************************/
    2452             : /*                         GetStructuralInfo()                          */
    2453             : /************************************************************************/
    2454             : 
    2455           5 : CSLConstList HDF5Array::GetStructuralInfo() const
    2456             : {
    2457           5 :     if (m_aosStructuralInfo.empty())
    2458             :     {
    2459             :         HDF5_GLOBAL_LOCK();
    2460           5 :         m_aosStructuralInfo = GetFilterInfo(m_hArray, /* nFilterMask = */ 0);
    2461             :     }
    2462           5 :     return m_aosStructuralInfo.List();
    2463             : }
    2464             : 
    2465             : /************************************************************************/
    2466             : /*                             CopyBuffer()                             */
    2467             : /************************************************************************/
    2468             : 
    2469          95 : static void CopyBuffer(size_t nDims, const size_t *count,
    2470             :                        const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    2471             :                        const GDALExtendedDataType &bufferDataType,
    2472             :                        GByte *pabySrc, void *pDstBuffer)
    2473             : {
    2474          95 :     const size_t nBufferDataTypeSize(bufferDataType.GetSize());
    2475         190 :     std::vector<size_t> anStackCount(nDims);
    2476         190 :     std::vector<GByte *> pabySrcBufferStack(nDims + 1);
    2477         190 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    2478         190 :     std::vector<GPtrDiff_t> anSrcStride(nDims);
    2479         190 :     std::vector<size_t> anSrcOffset(nDims + 1);
    2480          95 :     size_t nCurStride = nBufferDataTypeSize;
    2481         286 :     for (size_t i = nDims; i > 0;)
    2482             :     {
    2483         191 :         --i;
    2484         191 :         anSrcStride[i] = arrayStep[i] > 0
    2485         191 :                              ? nCurStride
    2486         105 :                              : -static_cast<GPtrDiff_t>(nCurStride);
    2487         191 :         anSrcOffset[i] = arrayStep[i] > 0 ? 0 : (count[i] - 1) * nCurStride;
    2488         191 :         nCurStride *= count[i];
    2489             :     }
    2490          95 :     pabySrcBufferStack[0] = pabySrc + anSrcOffset[0];
    2491          95 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    2492          95 :     size_t iDim = 0;
    2493        1469 : lbl_next_depth:
    2494        1469 :     if (iDim == nDims)
    2495             :     {
    2496        1113 :         memcpy(pabyDstBufferStack[nDims], pabySrcBufferStack[nDims],
    2497             :                nBufferDataTypeSize);
    2498             :     }
    2499             :     else
    2500             :     {
    2501         356 :         anStackCount[iDim] = count[iDim];
    2502             :         while (true)
    2503             :         {
    2504        1374 :             ++iDim;
    2505        2748 :             pabySrcBufferStack[iDim] =
    2506        1374 :                 pabySrcBufferStack[iDim - 1] + anSrcOffset[iDim];
    2507        1374 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    2508        1374 :             goto lbl_next_depth;
    2509        1374 :         lbl_return_to_caller_in_loop:
    2510        1374 :             --iDim;
    2511        1374 :             --anStackCount[iDim];
    2512        1374 :             if (anStackCount[iDim] == 0)
    2513         356 :                 break;
    2514        1018 :             if (bufferStride[iDim] >= 0)
    2515         978 :                 pabyDstBufferStack[iDim] +=
    2516         978 :                     bufferStride[iDim] * nBufferDataTypeSize;
    2517             :             else
    2518          40 :                 pabyDstBufferStack[iDim] -=
    2519          40 :                     (-bufferStride[iDim]) * nBufferDataTypeSize;
    2520        1018 :             pabySrcBufferStack[iDim] += anSrcStride[iDim];
    2521             :         }
    2522             :     }
    2523        1469 :     if (iDim > 0)
    2524        1374 :         goto lbl_return_to_caller_in_loop;
    2525          95 : }
    2526             : 
    2527             : /************************************************************************/
    2528             : /*                              ReadSlow()                              */
    2529             : /************************************************************************/
    2530             : 
    2531         100 : bool HDF5Array::ReadSlow(const GUInt64 *arrayStartIdx, const size_t *count,
    2532             :                          const GInt64 *arrayStep,
    2533             :                          const GPtrDiff_t *bufferStride,
    2534             :                          const GDALExtendedDataType &bufferDataType,
    2535             :                          void *pDstBuffer) const
    2536             : {
    2537         100 :     const size_t nBufferDataTypeSize(bufferDataType.GetSize());
    2538         100 :     if (nBufferDataTypeSize == 0)
    2539           0 :         return false;
    2540         100 :     const size_t nDims(m_dims.size());
    2541         100 :     size_t nEltCount = 1;
    2542         301 :     for (size_t i = 0; i < nDims; ++i)
    2543             :     {
    2544         201 :         nEltCount *= count[i];
    2545             :     }
    2546             : 
    2547             :     // Only for testing
    2548             :     const char *pszThreshold =
    2549         100 :         CPLGetConfigOption("GDAL_HDF5_TEMP_ARRAY_ALLOC_SIZE", "16777216");
    2550             :     const GUIntBig nThreshold =
    2551         100 :         CPLScanUIntBig(pszThreshold, static_cast<int>(strlen(pszThreshold)));
    2552         100 :     if (nEltCount == 1 || nEltCount <= nThreshold / nBufferDataTypeSize)
    2553             :     {
    2554          95 :         CPLDebug("HDF5", "Using slow path");
    2555         190 :         std::vector<GByte> abyTemp;
    2556             :         try
    2557             :         {
    2558          95 :             abyTemp.resize(nEltCount * nBufferDataTypeSize);
    2559             :         }
    2560           0 :         catch (const std::exception &e)
    2561             :         {
    2562           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    2563           0 :             return false;
    2564             :         }
    2565         190 :         std::vector<GUInt64> anStart(nDims);
    2566         190 :         std::vector<GInt64> anStep(nDims);
    2567         286 :         for (size_t i = 0; i < nDims; i++)
    2568             :         {
    2569         191 :             if (arrayStep[i] >= 0)
    2570             :             {
    2571          87 :                 anStart[i] = arrayStartIdx[i];
    2572          87 :                 anStep[i] = arrayStep[i];
    2573             :             }
    2574             :             else
    2575             :             {
    2576             :                 // Use double negation so that operations occur only on
    2577             :                 // positive quantities to avoid an artificial negative signed
    2578             :                 // integer to unsigned conversion.
    2579         208 :                 anStart[i] =
    2580         104 :                     arrayStartIdx[i] - ((-arrayStep[i]) * (count[i] - 1));
    2581         104 :                 anStep[i] = -arrayStep[i];
    2582             :             }
    2583             :         }
    2584         190 :         std::vector<GPtrDiff_t> anStride(nDims);
    2585          95 :         size_t nCurStride = 1;
    2586         286 :         for (size_t i = nDims; i > 0;)
    2587             :         {
    2588         191 :             --i;
    2589         191 :             anStride[i] = nCurStride;
    2590         191 :             nCurStride *= count[i];
    2591             :         }
    2592          95 :         if (!IRead(anStart.data(), count, anStep.data(), anStride.data(),
    2593          95 :                    bufferDataType, &abyTemp[0]))
    2594             :         {
    2595           0 :             return false;
    2596             :         }
    2597          95 :         CopyBuffer(nDims, count, arrayStep, bufferStride, bufferDataType,
    2598          95 :                    &abyTemp[0], pDstBuffer);
    2599          95 :         return true;
    2600             :     }
    2601             : 
    2602          10 :     std::vector<GUInt64> arrayStartIdxHalf;
    2603          10 :     std::vector<size_t> countHalf;
    2604           5 :     size_t iDimToSplit = nDims;
    2605             :     // Find the first dimension that has at least 2 elements, to split along
    2606             :     // it
    2607          15 :     for (size_t i = 0; i < nDims; ++i)
    2608             :     {
    2609          10 :         arrayStartIdxHalf.push_back(arrayStartIdx[i]);
    2610          10 :         countHalf.push_back(count[i]);
    2611          10 :         if (count[i] >= 2 && iDimToSplit == nDims)
    2612             :         {
    2613           5 :             iDimToSplit = i;
    2614             :         }
    2615             :     }
    2616             : 
    2617           5 :     CPLAssert(iDimToSplit != nDims);
    2618             : 
    2619           5 :     countHalf[iDimToSplit] /= 2;
    2620           5 :     if (!ReadSlow(arrayStartIdxHalf.data(), countHalf.data(), arrayStep,
    2621             :                   bufferStride, bufferDataType, pDstBuffer))
    2622             :     {
    2623           0 :         return false;
    2624             :     }
    2625           5 :     arrayStartIdxHalf[iDimToSplit] = static_cast<GUInt64>(
    2626           5 :         arrayStep[iDimToSplit] > 0
    2627           0 :             ? arrayStartIdx[iDimToSplit] +
    2628           0 :                   arrayStep[iDimToSplit] * countHalf[iDimToSplit]
    2629           5 :             : arrayStartIdx[iDimToSplit] -
    2630           5 :                   (-arrayStep[iDimToSplit]) * countHalf[iDimToSplit]);
    2631             :     GByte *pOtherHalfDstBuffer =
    2632          10 :         static_cast<GByte *>(pDstBuffer) + bufferStride[iDimToSplit] *
    2633           5 :                                                countHalf[iDimToSplit] *
    2634           5 :                                                nBufferDataTypeSize;
    2635           5 :     countHalf[iDimToSplit] = count[iDimToSplit] - countHalf[iDimToSplit];
    2636           5 :     return ReadSlow(arrayStartIdxHalf.data(), countHalf.data(), arrayStep,
    2637           5 :                     bufferStride, bufferDataType, pOtherHalfDstBuffer);
    2638             : }
    2639             : 
    2640             : /************************************************************************/
    2641             : /*                       IngestVariableStrings()                        */
    2642             : /************************************************************************/
    2643             : 
    2644           1 : static void IngestVariableStrings(void *pDstBuffer, hid_t hBufferType,
    2645             :                                   size_t nDims, const size_t *count,
    2646             :                                   const GPtrDiff_t *bufferStride)
    2647             : {
    2648           2 :     std::vector<hsize_t> anCountOne(nDims, 1);
    2649             :     const hid_t hMemSpaceOne =
    2650           1 :         nDims == 0 ? H5Screate(H5S_SCALAR)
    2651           1 :                    : H5Screate_simple(static_cast<int>(nDims),
    2652           1 :                                       anCountOne.data(), nullptr);
    2653           2 :     std::vector<size_t> anStackCount(nDims);
    2654           2 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    2655           1 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    2656           1 :     size_t iDim = 0;
    2657           4 : lbl_next_depth:
    2658           4 :     if (iDim == nDims)
    2659             :     {
    2660           2 :         void *old_ptr = pabyDstBufferStack[nDims];
    2661           2 :         const char *pszSrcStr = *static_cast<char **>(old_ptr);
    2662           2 :         char *pszNewStr = pszSrcStr ? VSIStrdup(pszSrcStr) : nullptr;
    2663           2 :         H5Dvlen_reclaim(hBufferType, hMemSpaceOne, H5P_DEFAULT, old_ptr);
    2664           2 :         *static_cast<char **>(old_ptr) = pszNewStr;
    2665             :     }
    2666             :     else
    2667             :     {
    2668           2 :         anStackCount[iDim] = count[iDim];
    2669             :         while (true)
    2670             :         {
    2671           3 :             ++iDim;
    2672           3 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    2673           3 :             goto lbl_next_depth;
    2674           3 :         lbl_return_to_caller_in_loop:
    2675           3 :             --iDim;
    2676           3 :             --anStackCount[iDim];
    2677           3 :             if (anStackCount[iDim] == 0)
    2678           2 :                 break;
    2679           1 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * sizeof(char *);
    2680             :         }
    2681             :     }
    2682           4 :     if (iDim > 0)
    2683           3 :         goto lbl_return_to_caller_in_loop;
    2684           1 :     H5Sclose(hMemSpaceOne);
    2685           1 : }
    2686             : 
    2687             : /************************************************************************/
    2688             : /*                      IngestFixedLengthStrings()                      */
    2689             : /************************************************************************/
    2690             : 
    2691           0 : static void IngestFixedLengthStrings(void *pDstBuffer, const void *pTemp,
    2692             :                                      hid_t hBufferType, size_t nDims,
    2693             :                                      const size_t *count,
    2694             :                                      const GPtrDiff_t *bufferStride)
    2695             : {
    2696           0 :     const size_t nStringSize = H5Tget_size(hBufferType);
    2697           0 :     std::vector<size_t> anStackCount(nDims);
    2698           0 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    2699           0 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pTemp);
    2700           0 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    2701           0 :     size_t iDim = 0;
    2702           0 :     const bool bSpacePad = H5Tget_strpad(hBufferType) == H5T_STR_SPACEPAD;
    2703           0 : lbl_next_depth:
    2704           0 :     if (iDim == nDims)
    2705             :     {
    2706           0 :         char *pszStr = static_cast<char *>(VSIMalloc(nStringSize + 1));
    2707           0 :         if (pszStr)
    2708             :         {
    2709           0 :             memcpy(pszStr, pabySrcBuffer, nStringSize);
    2710           0 :             size_t nIter = nStringSize;
    2711           0 :             if (bSpacePad)
    2712             :             {
    2713           0 :                 while (nIter >= 1 && pszStr[nIter - 1] == ' ')
    2714             :                 {
    2715           0 :                     nIter--;
    2716             :                 }
    2717             :             }
    2718           0 :             pszStr[nIter] = 0;
    2719             :         }
    2720           0 :         void *ptr = pabyDstBufferStack[nDims];
    2721           0 :         *static_cast<char **>(ptr) = pszStr;
    2722             :     }
    2723             :     else
    2724             :     {
    2725           0 :         anStackCount[iDim] = count[iDim];
    2726             :         while (true)
    2727             :         {
    2728           0 :             ++iDim;
    2729           0 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    2730           0 :             goto lbl_next_depth;
    2731           0 :         lbl_return_to_caller_in_loop:
    2732           0 :             --iDim;
    2733           0 :             --anStackCount[iDim];
    2734           0 :             if (anStackCount[iDim] == 0)
    2735           0 :                 break;
    2736           0 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * sizeof(char *);
    2737           0 :             pabySrcBuffer += nStringSize;
    2738             :         }
    2739             :     }
    2740           0 :     if (iDim > 0)
    2741           0 :         goto lbl_return_to_caller_in_loop;
    2742           0 : }
    2743             : 
    2744             : /************************************************************************/
    2745             : /*                  GetHDF5DataTypeFromGDALDataType()                   */
    2746             : /************************************************************************/
    2747             : 
    2748             : static hid_t
    2749        6144 : GetHDF5DataTypeFromGDALDataType(const GDALExtendedDataType &dt, hid_t hNativeDT,
    2750             :                                 const GDALExtendedDataType &bufferDataType)
    2751             : {
    2752        6144 :     hid_t hBufferType = H5I_INVALID_HID;
    2753        6144 :     switch (bufferDataType.GetNumericDataType())
    2754             :     {
    2755          30 :         case GDT_UInt8:
    2756          30 :             hBufferType = H5Tcopy(H5T_NATIVE_UCHAR);
    2757          30 :             break;
    2758           0 :         case GDT_Int8:
    2759           0 :             hBufferType = H5Tcopy(H5T_NATIVE_SCHAR);
    2760           0 :             break;
    2761          21 :         case GDT_UInt16:
    2762          21 :             hBufferType = H5Tcopy(H5T_NATIVE_USHORT);
    2763          21 :             break;
    2764          18 :         case GDT_Int16:
    2765          18 :             hBufferType = H5Tcopy(H5T_NATIVE_SHORT);
    2766          18 :             break;
    2767          14 :         case GDT_UInt32:
    2768          14 :             hBufferType = H5Tcopy(H5T_NATIVE_UINT);
    2769          14 :             break;
    2770        2082 :         case GDT_Int32:
    2771        2082 :             hBufferType = H5Tcopy(H5T_NATIVE_INT);
    2772        2082 :             break;
    2773           1 :         case GDT_UInt64:
    2774           1 :             hBufferType = H5Tcopy(H5T_NATIVE_UINT64);
    2775           1 :             break;
    2776           2 :         case GDT_Int64:
    2777           2 :             hBufferType = H5Tcopy(H5T_NATIVE_INT64);
    2778           2 :             break;
    2779           0 :         case GDT_Float16:
    2780             : #ifdef HDF5_HAVE_FLOAT16
    2781             :             hBufferType = H5Tcopy(H5T_NATIVE_FLOAT16);
    2782             :             break;
    2783             : #else
    2784           0 :             return H5I_INVALID_HID;
    2785             : #endif
    2786          22 :         case GDT_Float32:
    2787          22 :             hBufferType = H5Tcopy(H5T_NATIVE_FLOAT);
    2788          22 :             break;
    2789        3954 :         case GDT_Float64:
    2790        3954 :             hBufferType = H5Tcopy(H5T_NATIVE_DOUBLE);
    2791        3954 :             break;
    2792           0 :         case GDT_CInt16:
    2793             :         case GDT_CInt32:
    2794             :         case GDT_CFloat16:
    2795             :         case GDT_CFloat32:
    2796             :         case GDT_CFloat64:
    2797           0 :             if (bufferDataType != dt)
    2798             :             {
    2799           0 :                 return H5I_INVALID_HID;
    2800             :             }
    2801             :             else
    2802             :             {
    2803           0 :                 hBufferType = H5Tcopy(hNativeDT);
    2804           0 :                 break;
    2805             :             }
    2806           0 :         case GDT_Unknown:
    2807             :         case GDT_TypeCount:
    2808           0 :             return H5I_INVALID_HID;
    2809             :     }
    2810        6144 :     return hBufferType;
    2811             : }
    2812             : 
    2813             : /************************************************************************/
    2814             : /*                         FreeDynamicMemory()                          */
    2815             : /************************************************************************/
    2816             : 
    2817        2424 : static void FreeDynamicMemory(GByte *pabyPtr, hid_t hDataType)
    2818             : {
    2819        2424 :     const auto klass = H5Tget_class(hDataType);
    2820        2424 :     if (klass == H5T_STRING && H5Tis_variable_str(hDataType))
    2821             :     {
    2822        1139 :         auto hDataSpace = H5Screate(H5S_SCALAR);
    2823        1139 :         H5Dvlen_reclaim(hDataType, hDataSpace, H5P_DEFAULT, pabyPtr);
    2824        1139 :         H5Sclose(hDataSpace);
    2825             :     }
    2826        1285 :     else if (klass == H5T_COMPOUND)
    2827             :     {
    2828         469 :         const unsigned nMembers = H5Tget_nmembers(hDataType);
    2829        2424 :         for (unsigned i = 0; i < nMembers; i++)
    2830             :         {
    2831        1955 :             const auto nOffset = H5Tget_member_offset(hDataType, i);
    2832        1955 :             auto hMemberType = H5Tget_member_type(hDataType, i);
    2833        1955 :             if (hMemberType < 0)
    2834           0 :                 continue;
    2835        1955 :             FreeDynamicMemory(pabyPtr + nOffset, hMemberType);
    2836        1955 :             H5Tclose(hMemberType);
    2837             :         }
    2838             :     }
    2839        2424 : }
    2840             : 
    2841             : /************************************************************************/
    2842             : /*                   CreateMapTargetComponentsToSrc()                   */
    2843             : /************************************************************************/
    2844             : 
    2845             : static std::vector<unsigned>
    2846         454 : CreateMapTargetComponentsToSrc(hid_t hSrcDataType,
    2847             :                                const GDALExtendedDataType &dstDataType)
    2848             : {
    2849         454 :     CPLAssert(H5Tget_class(hSrcDataType) == H5T_COMPOUND);
    2850         454 :     CPLAssert(dstDataType.GetClass() == GEDTC_COMPOUND);
    2851             : 
    2852         454 :     const unsigned nMembers = H5Tget_nmembers(hSrcDataType);
    2853         908 :     std::map<std::string, unsigned> oMapSrcCompNameToIdx;
    2854        2381 :     for (unsigned i = 0; i < nMembers; i++)
    2855             :     {
    2856        1927 :         char *pszName = H5Tget_member_name(hSrcDataType, i);
    2857        1927 :         if (pszName)
    2858             :         {
    2859        1927 :             oMapSrcCompNameToIdx[pszName] = i;
    2860        1927 :             H5free_memory(pszName);
    2861             :         }
    2862             :     }
    2863             : 
    2864         454 :     std::vector<unsigned> ret;
    2865         454 :     const auto &comps = dstDataType.GetComponents();
    2866         454 :     ret.reserve(comps.size());
    2867        2163 :     for (const auto &comp : comps)
    2868             :     {
    2869        1709 :         auto oIter = oMapSrcCompNameToIdx.find(comp->GetName());
    2870        1709 :         CPLAssert(oIter != oMapSrcCompNameToIdx.end());
    2871        1709 :         ret.emplace_back(oIter->second);
    2872             :     }
    2873         908 :     return ret;
    2874             : }
    2875             : 
    2876             : /************************************************************************/
    2877             : /*                             CopyValue()                              */
    2878             : /************************************************************************/
    2879             : 
    2880       13642 : static void CopyValue(const GByte *pabySrcBuffer, hid_t hSrcDataType,
    2881             :                       GByte *pabyDstBuffer,
    2882             :                       const GDALExtendedDataType &dstDataType,
    2883             :                       const std::vector<unsigned> &mapDstCompsToSrcComps)
    2884             : {
    2885       13642 :     const auto klass = H5Tget_class(hSrcDataType);
    2886       13642 :     if (klass == H5T_STRING)
    2887             :     {
    2888        3122 :         if (H5Tis_variable_str(hSrcDataType))
    2889             :         {
    2890        2341 :             GDALExtendedDataType::CopyValue(
    2891        4682 :                 pabySrcBuffer, GDALExtendedDataType::CreateString(),
    2892             :                 pabyDstBuffer, dstDataType);
    2893             :         }
    2894             :         else
    2895             :         {
    2896         781 :             size_t nStringSize = H5Tget_size(hSrcDataType);
    2897         781 :             char *pszStr = static_cast<char *>(VSIMalloc(nStringSize + 1));
    2898         781 :             if (pszStr)
    2899             :             {
    2900         781 :                 memcpy(pszStr, pabySrcBuffer, nStringSize);
    2901         781 :                 pszStr[nStringSize] = 0;
    2902             :             }
    2903         781 :             GDALExtendedDataType::CopyValue(
    2904        1562 :                 &pszStr, GDALExtendedDataType::CreateString(), pabyDstBuffer,
    2905             :                 dstDataType);
    2906         781 :             CPLFree(pszStr);
    2907             :         }
    2908             :     }
    2909       10520 :     else if (klass == H5T_COMPOUND)
    2910             :     {
    2911         474 :         if (dstDataType.GetClass() != GEDTC_COMPOUND)
    2912             :         {
    2913           3 :             const auto eSrcDataType = ::HDF5Dataset::GetDataType(hSrcDataType);
    2914             :             // Typically source is complex data type
    2915             : #ifdef HDF5_HAVE_FLOAT16
    2916             :             if (eSrcDataType == GDT_CFloat32 &&
    2917             :                 ::HDF5Dataset::IsNativeCFloat16(hSrcDataType))
    2918             :             {
    2919             :                 if (dstDataType.GetNumericDataType() == GDT_CFloat32)
    2920             :                 {
    2921             :                     for (int j = 0; j <= 1; ++j)
    2922             :                     {
    2923             :                         uint16_t nVal16;
    2924             :                         memcpy(&nVal16, pabySrcBuffer + j * sizeof(nVal16),
    2925             :                                sizeof(nVal16));
    2926             :                         const uint32_t nVal32 = CPLHalfToFloat(nVal16);
    2927             :                         memcpy(pabyDstBuffer + j * sizeof(float), &nVal32,
    2928             :                                sizeof(nVal32));
    2929             :                     }
    2930             :                 }
    2931             :                 else if (dstDataType.GetNumericDataType() == GDT_CFloat64)
    2932             :                 {
    2933             :                     for (int j = 0; j <= 1; ++j)
    2934             :                     {
    2935             :                         uint16_t nVal16;
    2936             :                         memcpy(&nVal16, pabySrcBuffer + j * sizeof(nVal16),
    2937             :                                sizeof(nVal16));
    2938             :                         const uint32_t nVal32 = CPLHalfToFloat(nVal16);
    2939             :                         float fVal;
    2940             :                         memcpy(&fVal, &nVal32, sizeof(fVal));
    2941             :                         double dfVal = fVal;
    2942             :                         memcpy(pabyDstBuffer + j * sizeof(double), &dfVal,
    2943             :                                sizeof(dfVal));
    2944             :                     }
    2945             :                 }
    2946             :                 return;
    2947             :             }
    2948             : 
    2949             : #endif
    2950           6 :             auto srcDataType(GDALExtendedDataType::Create(eSrcDataType));
    2951           6 :             if (srcDataType.GetClass() == GEDTC_NUMERIC &&
    2952           3 :                 srcDataType.GetNumericDataType() != GDT_Unknown)
    2953             :             {
    2954           3 :                 GDALExtendedDataType::CopyValue(pabySrcBuffer, srcDataType,
    2955             :                                                 pabyDstBuffer, dstDataType);
    2956             :             }
    2957             :         }
    2958             :         else
    2959             :         {
    2960         471 :             const auto &comps = dstDataType.GetComponents();
    2961         471 :             CPLAssert(comps.size() == mapDstCompsToSrcComps.size());
    2962        2197 :             for (size_t iDst = 0; iDst < comps.size(); ++iDst)
    2963             :             {
    2964        1726 :                 const unsigned iSrc = mapDstCompsToSrcComps[iDst];
    2965        1726 :                 auto hMemberType = H5Tget_member_type(hSrcDataType, iSrc);
    2966             :                 const std::vector<unsigned> mapDstSubCompsToSrcSubComps(
    2967        1726 :                     (H5Tget_class(hMemberType) == H5T_COMPOUND &&
    2968           0 :                      comps[iDst]->GetType().GetClass() == GEDTC_COMPOUND)
    2969        1726 :                         ? CreateMapTargetComponentsToSrc(hMemberType,
    2970           0 :                                                          comps[iDst]->GetType())
    2971        3452 :                         : std::vector<unsigned>());
    2972        1726 :                 CopyValue(pabySrcBuffer +
    2973        1726 :                               H5Tget_member_offset(hSrcDataType, iSrc),
    2974        1726 :                           hMemberType, pabyDstBuffer + comps[iDst]->GetOffset(),
    2975        1726 :                           comps[iDst]->GetType(), mapDstSubCompsToSrcSubComps);
    2976        1726 :                 H5Tclose(hMemberType);
    2977             :             }
    2978             :         }
    2979             :     }
    2980       10046 :     else if (klass == H5T_ENUM)
    2981             :     {
    2982        1454 :         auto hParent = H5Tget_super(hSrcDataType);
    2983        1454 :         CopyValue(pabySrcBuffer, hParent, pabyDstBuffer, dstDataType, {});
    2984        1454 :         H5Tclose(hParent);
    2985             :     }
    2986             : #ifdef HDF5_HAVE_FLOAT16
    2987             :     else if (H5Tequal(hSrcDataType, H5T_NATIVE_FLOAT16))
    2988             :     {
    2989             :         uint16_t nVal16;
    2990             :         memcpy(&nVal16, pabySrcBuffer, sizeof(nVal16));
    2991             :         const uint32_t nVal32 = CPLHalfToFloat(nVal16);
    2992             :         float fVal;
    2993             :         memcpy(&fVal, &nVal32, sizeof(fVal));
    2994             :         GDALExtendedDataType::CopyValue(
    2995             :             &fVal, GDALExtendedDataType::Create(GDT_Float32), pabyDstBuffer,
    2996             :             dstDataType);
    2997             :     }
    2998             : #endif
    2999             :     else
    3000             :     {
    3001        8592 :         GDALDataType eDT = ::HDF5Dataset::GetDataType(hSrcDataType);
    3002        8592 :         GDALExtendedDataType::CopyValue(pabySrcBuffer,
    3003       17184 :                                         GDALExtendedDataType::Create(eDT),
    3004             :                                         pabyDstBuffer, dstDataType);
    3005             :     }
    3006       13642 : }
    3007             : 
    3008             : /************************************************************************/
    3009             : /*                         CopyToFinalBuffer()                          */
    3010             : /************************************************************************/
    3011             : 
    3012         454 : static void CopyToFinalBuffer(void *pDstBuffer, const void *pTemp, size_t nDims,
    3013             :                               const size_t *count,
    3014             :                               const GPtrDiff_t *bufferStride,
    3015             :                               hid_t hSrcDataType,
    3016             :                               const GDALExtendedDataType &bufferDataType)
    3017             : {
    3018         454 :     const size_t nSrcDataTypeSize(H5Tget_size(hSrcDataType));
    3019         908 :     std::vector<size_t> anStackCount(nDims);
    3020         908 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    3021         454 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pTemp);
    3022         454 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    3023         454 :     size_t iDim = 0;
    3024             :     const std::vector<unsigned> mapDstCompsToSrcComps(
    3025         907 :         (H5Tget_class(hSrcDataType) == H5T_COMPOUND &&
    3026         453 :          bufferDataType.GetClass() == GEDTC_COMPOUND)
    3027         454 :             ? CreateMapTargetComponentsToSrc(hSrcDataType, bufferDataType)
    3028         908 :             : std::vector<unsigned>());
    3029             : 
    3030         454 :     bool bFastCopyOfCompoundToSingleComponentCompound = false;
    3031         454 :     GDALDataType eSrcTypeComp = GDT_Unknown;
    3032         454 :     size_t nSrcOffset = 0;
    3033         454 :     GDALDataType eDstTypeComp = GDT_Unknown;
    3034         454 :     int bufferStrideLastDim = 0;
    3035         670 :     if (nDims > 0 && mapDstCompsToSrcComps.size() == 1 &&
    3036         216 :         bufferDataType.GetComponents()[0]->GetType().GetClass() ==
    3037             :             GEDTC_NUMERIC)
    3038             :     {
    3039             :         auto hMemberType =
    3040         158 :             H5Tget_member_type(hSrcDataType, mapDstCompsToSrcComps[0]);
    3041         158 :         eSrcTypeComp = HDF5Dataset::GetDataType(hMemberType);
    3042         158 :         if (eSrcTypeComp != GDT_Unknown)
    3043             :         {
    3044             :             nSrcOffset =
    3045         145 :                 H5Tget_member_offset(hSrcDataType, mapDstCompsToSrcComps[0]);
    3046         145 :             eDstTypeComp = bufferDataType.GetComponents()[0]
    3047         145 :                                ->GetType()
    3048         145 :                                .GetNumericDataType();
    3049         290 :             bufferStrideLastDim = static_cast<int>(bufferStride[nDims - 1] *
    3050         145 :                                                    bufferDataType.GetSize());
    3051         145 :             bFastCopyOfCompoundToSingleComponentCompound = true;
    3052             :         }
    3053             :     }
    3054             : 
    3055         296 : lbl_next_depth:
    3056        1270 :     if (bFastCopyOfCompoundToSingleComponentCompound && iDim == nDims - 1)
    3057             :     {
    3058         285 :         GDALCopyWords64(pabySrcBuffer + nSrcOffset, eSrcTypeComp,
    3059             :                         static_cast<int>(nSrcDataTypeSize),
    3060         285 :                         pabyDstBufferStack[iDim], eDstTypeComp,
    3061         285 :                         static_cast<int>(bufferStrideLastDim), count[iDim]);
    3062         285 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    3063             :     }
    3064         985 :     else if (iDim == nDims)
    3065             :     {
    3066         530 :         CopyValue(pabySrcBuffer, hSrcDataType, pabyDstBufferStack[nDims],
    3067             :                   bufferDataType, mapDstCompsToSrcComps);
    3068         530 :         pabySrcBuffer += nSrcDataTypeSize;
    3069             :     }
    3070             :     else
    3071             :     {
    3072         455 :         anStackCount[iDim] = count[iDim];
    3073             :         while (true)
    3074             :         {
    3075         816 :             ++iDim;
    3076         816 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    3077         816 :             goto lbl_next_depth;
    3078         816 :         lbl_return_to_caller_in_loop:
    3079         816 :             --iDim;
    3080         816 :             --anStackCount[iDim];
    3081         816 :             if (anStackCount[iDim] == 0)
    3082         455 :                 break;
    3083         361 :             pabyDstBufferStack[iDim] +=
    3084         361 :                 bufferStride[iDim] * bufferDataType.GetSize();
    3085             :         }
    3086             :     }
    3087        1270 :     if (iDim > 0)
    3088         816 :         goto lbl_return_to_caller_in_loop;
    3089         454 : }
    3090             : 
    3091             : /************************************************************************/
    3092             : /*                               IRead()                                */
    3093             : /************************************************************************/
    3094             : 
    3095         898 : bool HDF5Array::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    3096             :                       const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    3097             :                       const GDALExtendedDataType &bufferDataType,
    3098             :                       void *pDstBuffer) const
    3099             : {
    3100             :     HDF5_GLOBAL_LOCK();
    3101             : 
    3102         898 :     const size_t nDims(m_dims.size());
    3103        1796 :     std::vector<H5OFFSET_TYPE> anOffset(nDims);
    3104        1796 :     std::vector<hsize_t> anCount(nDims);
    3105        1796 :     std::vector<hsize_t> anStep(nDims);
    3106             : 
    3107         898 :     size_t nEltCount = 1;
    3108        1881 :     for (size_t i = 0; i < nDims; ++i)
    3109             :     {
    3110        1073 :         if (count[i] != 1 && (arrayStep[i] < 0 || bufferStride[i] < 0))
    3111             :         {
    3112          90 :             return ReadSlow(arrayStartIdx, count, arrayStep, bufferStride,
    3113          90 :                             bufferDataType, pDstBuffer);
    3114             :         }
    3115         983 :         anOffset[i] = static_cast<hsize_t>(arrayStartIdx[i]);
    3116         983 :         anCount[i] = static_cast<hsize_t>(count[i]);
    3117         983 :         anStep[i] = static_cast<hsize_t>(count[i] == 1 ? 1 : arrayStep[i]);
    3118         983 :         nEltCount *= count[i];
    3119             :     }
    3120             : 
    3121         808 :     if (IsTransposedRequest(count, bufferStride))
    3122             :     {
    3123         267 :         return ReadForTransposedRequest(arrayStartIdx, count, arrayStep,
    3124             :                                         bufferStride, bufferDataType,
    3125         267 :                                         pDstBuffer);
    3126             :     }
    3127             : 
    3128         541 :     bool strideOK = true;
    3129         541 :     GPtrDiff_t nExpectedBufferStride = 1;
    3130        1248 :     for (size_t i = nDims; i != 0;)
    3131             :     {
    3132         708 :         --i;
    3133         708 :         if (count[i] != 1 && bufferStride[i] != nExpectedBufferStride)
    3134             :         {
    3135           1 :             strideOK = false;
    3136           1 :             break;
    3137             :         }
    3138         707 :         nExpectedBufferStride *= count[i];
    3139             :     }
    3140             : 
    3141         541 :     hid_t hBufferType = H5I_INVALID_HID;
    3142         541 :     GByte *pabyTemp = nullptr;
    3143         541 :     bool bUseTmpBuffer = false;
    3144         541 :     if (m_dt.GetClass() == GEDTC_STRING)
    3145             :     {
    3146           1 :         if (bufferDataType.GetClass() != GEDTC_STRING)
    3147             :         {
    3148           0 :             return false;
    3149             :         }
    3150           1 :         hBufferType = H5Tcopy(m_hNativeDT);
    3151           1 :         if (!H5Tis_variable_str(m_hNativeDT))
    3152             :         {
    3153           0 :             const size_t nStringSize = H5Tget_size(m_hNativeDT);
    3154             :             pabyTemp = static_cast<GByte *>(
    3155           0 :                 VSI_MALLOC2_VERBOSE(nStringSize, nEltCount));
    3156           0 :             if (pabyTemp == nullptr)
    3157           0 :                 return false;
    3158             :         }
    3159             :     }
    3160         611 :     else if (strideOK && bufferDataType.GetClass() == GEDTC_NUMERIC &&
    3161          72 :              m_dt.GetClass() == GEDTC_NUMERIC &&
    3162        1146 :              !GDALDataTypeIsComplex(m_dt.GetNumericDataType()) &&
    3163          67 :              !GDALDataTypeIsComplex(bufferDataType.GetNumericDataType()))
    3164             :     {
    3165             :         // Compatibility with older libhdf5 that doesn't like requesting
    3166             :         // an enum to an integer
    3167          67 :         if (H5Tget_class(m_hNativeDT) == H5T_ENUM)
    3168             :         {
    3169           0 :             auto hParent = H5Tget_super(m_hNativeDT);
    3170           0 :             if (H5Tequal(hParent, H5T_NATIVE_UCHAR) ||
    3171           0 :                 H5Tequal(hParent, H5T_NATIVE_SCHAR) ||
    3172           0 :                 H5Tequal(hParent, H5T_NATIVE_USHORT) ||
    3173           0 :                 H5Tequal(hParent, H5T_NATIVE_SHORT) ||
    3174           0 :                 H5Tequal(hParent, H5T_NATIVE_UINT) ||
    3175           0 :                 H5Tequal(hParent, H5T_NATIVE_INT) ||
    3176           0 :                 H5Tequal(hParent, H5T_NATIVE_UINT64) ||
    3177           0 :                 H5Tequal(hParent, H5T_NATIVE_INT64))
    3178             :             {
    3179           0 :                 hBufferType = H5Tcopy(m_hNativeDT);
    3180           0 :                 if (m_dt != bufferDataType)
    3181             :                 {
    3182           0 :                     bUseTmpBuffer = true;
    3183             :                 }
    3184             :             }
    3185           0 :             H5Tclose(hParent);
    3186             :         }
    3187          67 :         if (hBufferType == H5I_INVALID_HID)
    3188             :         {
    3189          67 :             hBufferType = GetHDF5DataTypeFromGDALDataType(m_dt, m_hNativeDT,
    3190             :                                                           bufferDataType);
    3191          67 :             if (hBufferType == H5I_INVALID_HID)
    3192             :             {
    3193           0 :                 return false;
    3194             :             }
    3195             :         }
    3196             :     }
    3197         473 :     else if (strideOK)
    3198             :     {
    3199         472 :         hBufferType = H5Tcopy(m_hNativeDT);
    3200         472 :         if (m_dt != bufferDataType || m_bHasString || m_bHasNonNativeDataType)
    3201             :         {
    3202         453 :             bUseTmpBuffer = true;
    3203             :         }
    3204             :     }
    3205             :     else
    3206             :     {
    3207           1 :         hBufferType = H5Tcopy(m_hNativeDT);
    3208           1 :         bUseTmpBuffer = true;
    3209             :     }
    3210             : 
    3211         541 :     if (bUseTmpBuffer)
    3212             :     {
    3213         454 :         const size_t nDataTypeSize = H5Tget_size(hBufferType);
    3214             :         pabyTemp =
    3215         454 :             static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nDataTypeSize, nEltCount));
    3216         454 :         if (pabyTemp == nullptr)
    3217             :         {
    3218           0 :             H5Tclose(hBufferType);
    3219           0 :             return false;
    3220             :         }
    3221             :     }
    3222             : 
    3223             :     // Select block from file space.
    3224             :     herr_t status;
    3225         541 :     if (nDims)
    3226             :     {
    3227             :         status =
    3228         539 :             H5Sselect_hyperslab(m_hDataSpace, H5S_SELECT_SET, anOffset.data(),
    3229         539 :                                 anStep.data(), anCount.data(), nullptr);
    3230         539 :         if (status < 0)
    3231             :         {
    3232           0 :             H5Tclose(hBufferType);
    3233           0 :             VSIFree(pabyTemp);
    3234           0 :             return false;
    3235             :         }
    3236             :     }
    3237             : 
    3238             :     // Create memory data space
    3239             :     const hid_t hMemSpace = nDims == 0
    3240         541 :                                 ? H5Screate(H5S_SCALAR)
    3241         539 :                                 : H5Screate_simple(static_cast<int>(nDims),
    3242         539 :                                                    anCount.data(), nullptr);
    3243         541 :     if (nDims)
    3244             :     {
    3245         539 :         std::vector<H5OFFSET_TYPE> anMemOffset(nDims);
    3246             :         status =
    3247         539 :             H5Sselect_hyperslab(hMemSpace, H5S_SELECT_SET, anMemOffset.data(),
    3248         539 :                                 nullptr, anCount.data(), nullptr);
    3249         539 :         if (status < 0)
    3250             :         {
    3251           0 :             H5Tclose(hBufferType);
    3252           0 :             H5Sclose(hMemSpace);
    3253           0 :             VSIFree(pabyTemp);
    3254           0 :             return false;
    3255             :         }
    3256             :     }
    3257             : 
    3258         541 :     status = H5Dread(m_hArray, hBufferType, hMemSpace, m_hDataSpace,
    3259             :                      H5P_DEFAULT, pabyTemp ? pabyTemp : pDstBuffer);
    3260             : 
    3261         541 :     if (status >= 0)
    3262             :     {
    3263         541 :         if (H5Tis_variable_str(hBufferType))
    3264             :         {
    3265           1 :             IngestVariableStrings(pDstBuffer, hBufferType, nDims, count,
    3266             :                                   bufferStride);
    3267             :         }
    3268         540 :         else if (pabyTemp && bufferDataType.GetClass() == GEDTC_STRING)
    3269             :         {
    3270           0 :             IngestFixedLengthStrings(pDstBuffer, pabyTemp, hBufferType, nDims,
    3271             :                                      count, bufferStride);
    3272             :         }
    3273         540 :         else if (pabyTemp)
    3274             :         {
    3275         454 :             CopyToFinalBuffer(pDstBuffer, pabyTemp, nDims, count, bufferStride,
    3276         454 :                               m_hNativeDT, bufferDataType);
    3277             : 
    3278         454 :             if (m_bHasString)
    3279             :             {
    3280         353 :                 const size_t nBufferTypeSize = H5Tget_size(hBufferType);
    3281         353 :                 GByte *pabyPtr = pabyTemp;
    3282         822 :                 for (size_t i = 0; i < nEltCount; ++i)
    3283             :                 {
    3284         469 :                     FreeDynamicMemory(pabyPtr, hBufferType);
    3285         469 :                     pabyPtr += nBufferTypeSize;
    3286             :                 }
    3287             :             }
    3288             :         }
    3289             :     }
    3290             : 
    3291         541 :     H5Tclose(hBufferType);
    3292         541 :     H5Sclose(hMemSpace);
    3293         541 :     VSIFree(pabyTemp);
    3294             : 
    3295         541 :     return status >= 0;
    3296             : }
    3297             : 
    3298             : /************************************************************************/
    3299             : /*                           ~HDF5Attribute()                           */
    3300             : /************************************************************************/
    3301             : 
    3302      318122 : HDF5Attribute::~HDF5Attribute()
    3303             : {
    3304             :     HDF5_GLOBAL_LOCK();
    3305             : 
    3306      159061 :     if (m_hAttribute > 0)
    3307      159061 :         H5Aclose(m_hAttribute);
    3308      159061 :     if (m_hNativeDT > 0)
    3309      159061 :         H5Tclose(m_hNativeDT);
    3310      159061 :     if (m_hDataSpace > 0)
    3311      159061 :         H5Sclose(m_hDataSpace);
    3312      318122 : }
    3313             : 
    3314             : /************************************************************************/
    3315             : /*                       CopyAllAttrValuesInto()                        */
    3316             : /************************************************************************/
    3317             : 
    3318        9905 : static void CopyAllAttrValuesInto(size_t nDims, const GUInt64 *arrayStartIdx,
    3319             :                                   const size_t *count, const GInt64 *arrayStep,
    3320             :                                   const GPtrDiff_t *bufferStride,
    3321             :                                   const GDALExtendedDataType &bufferDataType,
    3322             :                                   void *pDstBuffer, hid_t hSrcBufferType,
    3323             :                                   const void *pabySrcBuffer)
    3324             : {
    3325        9905 :     const size_t nBufferDataTypeSize(bufferDataType.GetSize());
    3326        9905 :     const size_t nSrcDataTypeSize(H5Tget_size(hSrcBufferType));
    3327       19810 :     std::vector<size_t> anStackCount(nDims);
    3328       19810 :     std::vector<const GByte *> pabySrcBufferStack(nDims + 1);
    3329       19810 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    3330             :     const std::vector<unsigned> mapDstCompsToSrcComps(
    3331        9909 :         (H5Tget_class(hSrcBufferType) == H5T_COMPOUND &&
    3332           4 :          bufferDataType.GetClass() == GEDTC_COMPOUND)
    3333        9905 :             ? CreateMapTargetComponentsToSrc(hSrcBufferType, bufferDataType)
    3334       19810 :             : std::vector<unsigned>());
    3335             : 
    3336        9905 :     pabySrcBufferStack[0] = static_cast<const GByte *>(pabySrcBuffer);
    3337        9905 :     if (nDims > 0)
    3338          26 :         pabySrcBufferStack[0] += arrayStartIdx[0] * nSrcDataTypeSize;
    3339        9905 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    3340        9905 :     size_t iDim = 0;
    3341        9958 : lbl_next_depth:
    3342        9958 :     if (iDim == nDims)
    3343             :     {
    3344        9932 :         CopyValue(pabySrcBufferStack[nDims], hSrcBufferType,
    3345        9932 :                   pabyDstBufferStack[nDims], bufferDataType,
    3346             :                   mapDstCompsToSrcComps);
    3347             :     }
    3348             :     else
    3349             :     {
    3350          26 :         anStackCount[iDim] = count[iDim];
    3351             :         while (true)
    3352             :         {
    3353          53 :             ++iDim;
    3354          53 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    3355          53 :             pabySrcBufferStack[iDim] = pabySrcBufferStack[iDim - 1];
    3356          53 :             if (iDim < nDims)
    3357             :             {
    3358           0 :                 pabySrcBufferStack[iDim] +=
    3359           0 :                     arrayStartIdx[iDim] * nSrcDataTypeSize;
    3360             :             }
    3361          53 :             goto lbl_next_depth;
    3362          53 :         lbl_return_to_caller_in_loop:
    3363          53 :             --iDim;
    3364          53 :             --anStackCount[iDim];
    3365          53 :             if (anStackCount[iDim] == 0)
    3366          26 :                 break;
    3367          27 :             pabyDstBufferStack[iDim] +=
    3368          27 :                 bufferStride[iDim] * nBufferDataTypeSize;
    3369          27 :             pabySrcBufferStack[iDim] += arrayStep[iDim] * nSrcDataTypeSize;
    3370             :         }
    3371             :     }
    3372        9958 :     if (iDim > 0)
    3373          53 :         goto lbl_return_to_caller_in_loop;
    3374        9905 : }
    3375             : 
    3376             : /************************************************************************/
    3377             : /*                               IRead()                                */
    3378             : /************************************************************************/
    3379             : 
    3380        9905 : bool HDF5Attribute::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    3381             :                           const GInt64 *arrayStep,
    3382             :                           const GPtrDiff_t *bufferStride,
    3383             :                           const GDALExtendedDataType &bufferDataType,
    3384             :                           void *pDstBuffer) const
    3385             : {
    3386             :     HDF5_GLOBAL_LOCK();
    3387             : 
    3388        9905 :     const size_t nDims(m_dims.size());
    3389        9905 :     if (m_dt.GetClass() == GEDTC_STRING)
    3390             :     {
    3391        1640 :         if (bufferDataType.GetClass() != GEDTC_STRING)
    3392             :         {
    3393           0 :             return false;
    3394             :         }
    3395             : 
    3396        1640 :         if (!H5Tis_variable_str(m_hNativeDT))
    3397             :         {
    3398         329 :             const size_t nStringSize = H5Tget_size(m_hNativeDT);
    3399             :             GByte *pabyTemp = static_cast<GByte *>(
    3400         329 :                 VSI_CALLOC_VERBOSE(nStringSize, m_nElements));
    3401         329 :             if (pabyTemp == nullptr)
    3402           0 :                 return false;
    3403         658 :             if (H5Sget_simple_extent_type(m_hDataSpace) != H5S_NULL &&
    3404         329 :                 H5Aread(m_hAttribute, m_hNativeDT, pabyTemp) < 0)
    3405             :             {
    3406           0 :                 VSIFree(pabyTemp);
    3407           0 :                 return false;
    3408             :             }
    3409         329 :             CopyAllAttrValuesInto(nDims, arrayStartIdx, count, arrayStep,
    3410             :                                   bufferStride, bufferDataType, pDstBuffer,
    3411         329 :                                   m_hNativeDT, pabyTemp);
    3412         329 :             VSIFree(pabyTemp);
    3413             :         }
    3414             :         else
    3415             :         {
    3416        1311 :             void *pabyTemp = VSI_CALLOC_VERBOSE(sizeof(char *), m_nElements);
    3417        1311 :             if (pabyTemp == nullptr)
    3418           0 :                 return false;
    3419        2622 :             if (H5Sget_simple_extent_type(m_hDataSpace) != H5S_NULL &&
    3420        1311 :                 H5Aread(m_hAttribute, m_hNativeDT, pabyTemp) < 0)
    3421             :             {
    3422           0 :                 VSIFree(pabyTemp);
    3423           0 :                 return false;
    3424             :             }
    3425        1311 :             CopyAllAttrValuesInto(nDims, arrayStartIdx, count, arrayStep,
    3426             :                                   bufferStride, bufferDataType, pDstBuffer,
    3427        1311 :                                   m_hNativeDT, pabyTemp);
    3428        1311 :             H5Dvlen_reclaim(m_hNativeDT, m_hDataSpace, H5P_DEFAULT, pabyTemp);
    3429        1311 :             VSIFree(pabyTemp);
    3430             :         }
    3431        1640 :         return true;
    3432             :     }
    3433             : 
    3434        8265 :     hid_t hBufferType = H5I_INVALID_HID;
    3435       16529 :     if (m_dt.GetClass() == GEDTC_NUMERIC &&
    3436        8264 :         bufferDataType.GetClass() == GEDTC_NUMERIC &&
    3437       23410 :         !GDALDataTypeIsComplex(m_dt.GetNumericDataType()) &&
    3438        6881 :         !GDALDataTypeIsComplex(bufferDataType.GetNumericDataType()))
    3439             :     {
    3440             :         // Compatibility with older libhdf5 that doesn't like requesting
    3441             :         // an enum to an integer
    3442        6881 :         if (H5Tget_class(m_hNativeDT) == H5T_ENUM)
    3443             :         {
    3444         804 :             auto hParent = H5Tget_super(m_hNativeDT);
    3445         804 :             if (H5Tequal(hParent, H5T_NATIVE_UCHAR) ||
    3446           1 :                 H5Tequal(hParent, H5T_NATIVE_SCHAR) ||
    3447           1 :                 H5Tequal(hParent, H5T_NATIVE_USHORT) ||
    3448           1 :                 H5Tequal(hParent, H5T_NATIVE_SHORT) ||
    3449           1 :                 H5Tequal(hParent, H5T_NATIVE_UINT) ||
    3450           1 :                 H5Tequal(hParent, H5T_NATIVE_INT) ||
    3451         805 :                 H5Tequal(hParent, H5T_NATIVE_UINT64) ||
    3452           0 :                 H5Tequal(hParent, H5T_NATIVE_INT64))
    3453             :             {
    3454         804 :                 hBufferType = H5Tcopy(m_hNativeDT);
    3455             :             }
    3456         804 :             H5Tclose(hParent);
    3457             :         }
    3458        6881 :         if (hBufferType == H5I_INVALID_HID)
    3459             :         {
    3460        6077 :             hBufferType = GetHDF5DataTypeFromGDALDataType(m_dt, m_hNativeDT,
    3461             :                                                           bufferDataType);
    3462             :         }
    3463             :     }
    3464             :     else
    3465             :     {
    3466        1384 :         hBufferType = H5Tcopy(m_hNativeDT);
    3467             :     }
    3468             : 
    3469        8265 :     if (hBufferType == H5I_INVALID_HID)
    3470           0 :         return false;
    3471             : 
    3472        8265 :     const size_t nBufferTypeSize(H5Tget_size(hBufferType));
    3473             :     GByte *pabyTemp =
    3474        8265 :         static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBufferTypeSize, m_nElements));
    3475        8265 :     if (pabyTemp == nullptr)
    3476             :     {
    3477           0 :         H5Tclose(hBufferType);
    3478           0 :         return false;
    3479             :     }
    3480        8265 :     if (H5Aread(m_hAttribute, hBufferType, pabyTemp) < 0)
    3481             :     {
    3482           0 :         VSIFree(pabyTemp);
    3483           0 :         return false;
    3484             :     }
    3485        8265 :     CopyAllAttrValuesInto(nDims, arrayStartIdx, count, arrayStep, bufferStride,
    3486             :                           bufferDataType, pDstBuffer, hBufferType, pabyTemp);
    3487        8265 :     if (bufferDataType.GetClass() == GEDTC_COMPOUND && m_bHasString)
    3488             :     {
    3489           0 :         GByte *pabyPtr = pabyTemp;
    3490           0 :         for (size_t i = 0; i < m_nElements; ++i)
    3491             :         {
    3492           0 :             FreeDynamicMemory(pabyPtr, hBufferType);
    3493           0 :             pabyPtr += nBufferTypeSize;
    3494             :         }
    3495             :     }
    3496        8265 :     VSIFree(pabyTemp);
    3497        8265 :     H5Tclose(hBufferType);
    3498        8265 :     return true;
    3499             : }
    3500             : 
    3501             : /************************************************************************/
    3502             : /*                        GetIndexingVariable()                         */
    3503             : /************************************************************************/
    3504             : 
    3505          20 : std::shared_ptr<GDALMDArray> HDF5Dimension::GetIndexingVariable() const
    3506             : {
    3507             :     HDF5_GLOBAL_LOCK();
    3508             : 
    3509          20 :     auto hGroup = H5Gopen(m_poShared->GetHDF5(), m_osGroupFullname.c_str());
    3510          20 :     if (hGroup >= 0)
    3511             :     {
    3512          20 :         auto hArray = H5Dopen(hGroup, GetName().c_str());
    3513          20 :         H5Gclose(hGroup);
    3514          20 :         if (hArray >= 0)
    3515             :         {
    3516          40 :             auto ar(HDF5Array::Create(m_osGroupFullname, GetName(), m_poShared,
    3517          40 :                                       hArray, nullptr, false));
    3518          60 :             auto attrName = ar->GetAttribute("NAME");
    3519          20 :             if (attrName && attrName->GetDataType().GetClass() == GEDTC_STRING)
    3520             :             {
    3521          18 :                 const char *pszName = attrName->ReadAsString();
    3522          18 :                 if (pszName &&
    3523          18 :                     STARTS_WITH(
    3524             :                         pszName,
    3525             :                         "This is a netCDF dimension but not a netCDF variable"))
    3526             :                 {
    3527           0 :                     return nullptr;
    3528             :                 }
    3529             :             }
    3530          20 :             return ar;
    3531             :         }
    3532             :     }
    3533           0 :     return nullptr;
    3534             : }
    3535             : 
    3536             : }  // namespace GDAL
    3537             : 
    3538             : /************************************************************************/
    3539             : /*                            OpenMultiDim()                            */
    3540             : /************************************************************************/
    3541             : 
    3542          36 : GDALDataset *HDF5Dataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
    3543             : {
    3544             :     HDF5_GLOBAL_LOCK();
    3545             : 
    3546          36 :     const char *pszFilename = STARTS_WITH(poOpenInfo->pszFilename, "HDF5:")
    3547           8 :                                   ? poOpenInfo->pszFilename + strlen("HDF5:")
    3548             :                                   : poOpenInfo->pszFilename;
    3549             : 
    3550             :     // Try opening the dataset.
    3551          36 :     auto hHDF5 = GDAL_HDF5Open(pszFilename);
    3552          36 :     if (hHDF5 < 0)
    3553             :     {
    3554           1 :         return nullptr;
    3555             :     }
    3556             : 
    3557         105 :     auto poSharedResources = GDAL::HDF5SharedResources::Create(pszFilename);
    3558          35 :     poSharedResources->m_hHDF5 = hHDF5;
    3559             : 
    3560          70 :     auto poGroup(OpenGroup(poSharedResources));
    3561          35 :     if (poGroup == nullptr)
    3562             :     {
    3563           0 :         return nullptr;
    3564             :     }
    3565             : 
    3566          35 :     auto poDS(new HDF5Dataset());
    3567          35 :     poDS->m_poRootGroup = std::move(poGroup);
    3568             : 
    3569          35 :     poDS->SetDescription(poOpenInfo->pszFilename);
    3570             : 
    3571             :     // Setup/check for pam .aux.xml.
    3572          35 :     poDS->TryLoadXML();
    3573             : 
    3574          35 :     return poDS;
    3575             : }
    3576             : 
    3577             : /************************************************************************/
    3578             : /*                             OpenGroup()                              */
    3579             : /************************************************************************/
    3580             : 
    3581         349 : std::shared_ptr<GDALGroup> HDF5Dataset::OpenGroup(
    3582             :     const std::shared_ptr<GDAL::HDF5SharedResources> &poSharedResources)
    3583             : {
    3584             :     HDF5_GLOBAL_LOCK();
    3585             : 
    3586         698 :     auto poGroup = poSharedResources->GetRootGroup();
    3587         349 :     if (!poGroup)
    3588           0 :         return nullptr;
    3589             : 
    3590         349 :     if (HDF5EOSParser::HasHDFEOS(poGroup->GetID()))
    3591             :     {
    3592           5 :         poSharedResources->m_poHDF5EOSParser =
    3593          10 :             std::make_unique<HDF5EOSParser>();
    3594           5 :         if (poSharedResources->m_poHDF5EOSParser->Parse(poGroup->GetID()))
    3595             :         {
    3596           5 :             CPLDebug("HDF5", "Successfully parsed HDFEOS metadata");
    3597             :         }
    3598             :         else
    3599             :         {
    3600           0 :             poSharedResources->m_poHDF5EOSParser.reset();
    3601             :         }
    3602             :     }
    3603             : 
    3604         349 :     return poGroup;
    3605             : }

Generated by: LCOV version 1.14