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-05-06 01:13:38 Functions: 66 70 94.3 %

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

Generated by: LCOV version 1.14