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

Generated by: LCOV version 1.14