LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5multidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1467 1631 89.9 %
Date: 2025-11-04 19:10:06 Functions: 66 70 94.3 %

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

Generated by: LCOV version 1.14