LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5multidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1413 1564 90.3 %
Date: 2025-09-10 17:48:50 Functions: 63 67 94.0 %

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

Generated by: LCOV version 1.14