LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5multidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1489 1658 89.8 %
Date: 2026-04-15 22:10:00 Functions: 66 70 94.3 %

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

Generated by: LCOV version 1.14