LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 670 833 80.4 %
Date: 2026-05-04 15:01:57 Functions: 23 24 95.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  HDF5 Datasets. Open HDF5 file, fetch metadata and list of
       5             :  *           subdatasets.
       6             :  *           This driver initially based on code supplied by Markus Neteler
       7             :  * Author:  Denis Nadeau <denis.nadeau@gmail.com>
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
      11             :  * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #ifdef _POSIX_C_SOURCE
      17             : #undef _POSIX_C_SOURCE
      18             : #endif
      19             : 
      20             : #include "cpl_port.h"
      21             : 
      22             : #include "hdf5_api.h"
      23             : #include "hdf5dataset.h"
      24             : #include "hdf5drivercore.h"
      25             : #include "hdf5vfl.h"
      26             : 
      27             : #include <algorithm>
      28             : #include <stdio.h>
      29             : #include <string.h>
      30             : #include <string>
      31             : #include <cctype>
      32             : #include <limits>
      33             : 
      34             : #include "cpl_conv.h"
      35             : #include "cpl_error.h"
      36             : #include "cpl_float.h"
      37             : #include "cpl_string.h"
      38             : #include "gdal.h"
      39             : #include "gdal_frmts.h"
      40             : #include "gdal_priv.h"
      41             : 
      42             : constexpr size_t MAX_METADATA_LEN = 32768;
      43             : 
      44             : #ifdef ENABLE_HDF5_GLOBAL_LOCK
      45             : 
      46             : /************************************************************************/
      47             : /*                         GetHDF5GlobalMutex()                         */
      48             : /************************************************************************/
      49             : 
      50             : std::recursive_mutex &GetHDF5GlobalMutex()
      51             : {
      52             :     static std::recursive_mutex oMutex;
      53             :     return oMutex;
      54             : }
      55             : 
      56             : #endif
      57             : 
      58             : /************************************************************************/
      59             : /*                         HDF5GetFileDriver()                          */
      60             : /************************************************************************/
      61             : 
      62         600 : hid_t HDF5GetFileDriver()
      63             : {
      64         600 :     return HDF5VFLGetFileDriver();
      65             : }
      66             : 
      67             : /************************************************************************/
      68             : /*                        HDF5UnloadFileDriver()                        */
      69             : /************************************************************************/
      70             : 
      71          54 : void HDF5UnloadFileDriver()
      72             : {
      73          54 :     HDF5VFLUnloadFileDriver();
      74          54 : }
      75             : 
      76             : /************************************************************************/
      77             : /*                      HDF5DatasetDriverUnload()                       */
      78             : /************************************************************************/
      79             : 
      80           9 : static void HDF5DatasetDriverUnload(GDALDriver *)
      81             : {
      82           9 :     HDF5UnloadFileDriver();
      83           9 : }
      84             : 
      85             : /************************************************************************/
      86             : /* ==================================================================== */
      87             : /*                              HDF5Dataset                             */
      88             : /* ==================================================================== */
      89             : /************************************************************************/
      90             : 
      91             : /************************************************************************/
      92             : /*                         GDALRegister_HDF5()                          */
      93             : /************************************************************************/
      94          14 : void GDALRegister_HDF5()
      95             : 
      96             : {
      97          14 :     if (GDALGetDriverByName(HDF5_DRIVER_NAME) != nullptr)
      98           0 :         return;
      99             : 
     100          14 :     GDALDriver *poDriver = new GDALDriver();
     101             : 
     102          14 :     HDF5DriverSetCommonMetadata(poDriver);
     103             : 
     104          14 :     poDriver->pfnOpen = HDF5Dataset::Open;
     105          14 :     poDriver->pfnUnloadDriver = HDF5DatasetDriverUnload;
     106             : 
     107             : #if (defined(H5_VERS_MAJOR) &&                                                 \
     108             :      (H5_VERS_MAJOR >= 2 || (H5_VERS_MAJOR == 1 && H5_VERS_MINOR > 10) ||      \
     109             :       (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 10 && H5_VERS_RELEASE >= 5)))
     110             :     poDriver->SetMetadataItem("HAVE_H5Dget_chunk_info", "YES");
     111             : #endif
     112             : 
     113          14 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     114             : 
     115             : #ifdef HDF5_PLUGIN
     116          14 :     GDALRegister_HDF5Image();
     117          14 :     GDALRegister_BAG();
     118          14 :     GDALRegister_S102();
     119          14 :     GDALRegister_S104();
     120          14 :     GDALRegister_S111();
     121             : #endif
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                            HDF5Dataset()                             */
     126             : /************************************************************************/
     127         172 : HDF5Dataset::HDF5Dataset()
     128             :     : hGroupID(-1), papszSubDatasets(nullptr), nDatasetType(-1),
     129         172 :       nSubDataCount(0), poH5RootGroup(nullptr)
     130             : {
     131         172 : }
     132             : 
     133             : /************************************************************************/
     134             : /*                            ~HDF5Dataset()                            */
     135             : /************************************************************************/
     136         281 : HDF5Dataset::~HDF5Dataset()
     137             : {
     138             :     HDF5_GLOBAL_LOCK();
     139             : 
     140         172 :     if (hGroupID > 0)
     141         139 :         H5Gclose(hGroupID);
     142         172 :     if (m_hHDF5 > 0)
     143         139 :         H5Fclose(m_hHDF5);
     144             : 
     145         172 :     CSLDestroy(papszSubDatasets);
     146         172 :     if (poH5RootGroup != nullptr)
     147             :     {
     148         139 :         DestroyH5Objects(poH5RootGroup);
     149         139 :         CPLFree(poH5RootGroup->pszName);
     150         139 :         CPLFree(poH5RootGroup->pszPath);
     151         139 :         CPLFree(poH5RootGroup->pszUnderscorePath);
     152         139 :         CPLFree(poH5RootGroup->poHchild);
     153         139 :         CPLFree(poH5RootGroup);
     154             :     }
     155         281 : }
     156             : 
     157             : /************************************************************************/
     158             : /*                            GetDataType()                             */
     159             : /*                                                                      */
     160             : /*      Transform HDF5 datatype to GDAL datatype                        */
     161             : /************************************************************************/
     162      191076 : GDALDataType HDF5Dataset::GetDataType(hid_t TypeID)
     163             : {
     164             :     // Check for native types first
     165      191076 :     if (H5Tget_class(TypeID) != H5T_COMPOUND)
     166             :     {
     167             : 
     168      189476 :         if (H5Tequal(H5T_NATIVE_SCHAR, TypeID))
     169          78 :             return GDT_Int8;
     170      378796 :         else if (H5Tequal(H5T_NATIVE_CHAR, TypeID) ||
     171      189398 :                  H5Tequal(H5T_NATIVE_UCHAR, TypeID))
     172       20656 :             return GDT_UInt8;
     173      168742 :         else if (H5Tequal(H5T_NATIVE_SHORT, TypeID))
     174         791 :             return GDT_Int16;
     175      167951 :         else if (H5Tequal(H5T_NATIVE_USHORT, TypeID))
     176        2084 :             return GDT_UInt16;
     177      165867 :         else if (H5Tequal(H5T_NATIVE_INT, TypeID))
     178       12994 :             return GDT_Int32;
     179      152873 :         else if (H5Tequal(H5T_NATIVE_UINT, TypeID))
     180       26225 :             return GDT_UInt32;
     181      126648 :         else if (H5Tequal(H5T_NATIVE_INT64, TypeID))
     182        1529 :             return GDT_Int64;
     183      125119 :         else if (H5Tequal(H5T_NATIVE_UINT64, TypeID))
     184          39 :             return GDT_UInt64;
     185      125080 :         else if (H5Tequal(H5T_NATIVE_LONG, TypeID))
     186             :         {
     187             : #if SIZEOF_UNSIGNED_LONG == 4
     188             :             return GDT_Int32;
     189             : #else
     190           0 :             return GDT_Unknown;
     191             : #endif
     192             :         }
     193      125080 :         else if (H5Tequal(H5T_NATIVE_ULONG, TypeID))
     194             :         {
     195             : #if SIZEOF_UNSIGNED_LONG == 4
     196             :             return GDT_UInt32;
     197             : #else
     198           0 :             return GDT_Unknown;
     199             : #endif
     200             :         }
     201             : #ifdef HDF5_HAVE_FLOAT16
     202             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, TypeID))
     203             :             return GDT_Float32;
     204             : #endif
     205      125080 :         else if (H5Tequal(H5T_NATIVE_FLOAT, TypeID))
     206       37995 :             return GDT_Float32;
     207       87085 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID))
     208       38730 :             return GDT_Float64;
     209       48355 :         else if (H5Tequal(H5T_NATIVE_LLONG, TypeID))
     210           0 :             return GDT_Unknown;
     211       48355 :         else if (H5Tequal(H5T_NATIVE_ULLONG, TypeID))
     212           0 :             return GDT_Unknown;
     213             :     }
     214             :     else  // Parse compound type to determine if data is complex
     215             :     {
     216             :         // For complex the compound type must contain 2 elements
     217        1600 :         if (H5Tget_nmembers(TypeID) != 2)
     218         591 :             return GDT_Unknown;
     219             : 
     220             :         // For complex the native types of both elements should be the same
     221        1009 :         hid_t ElemTypeID = H5Tget_member_type(TypeID, 0);
     222        1009 :         hid_t Elem2TypeID = H5Tget_member_type(TypeID, 1);
     223        1009 :         const bool bTypeEqual = H5Tequal(ElemTypeID, Elem2TypeID) > 0;
     224        1009 :         H5Tclose(Elem2TypeID);
     225        1009 :         if (!bTypeEqual)
     226             :         {
     227         599 :             H5Tclose(ElemTypeID);
     228         599 :             return GDT_Unknown;
     229             :         }
     230             : 
     231         410 :         char *pszName1 = H5Tget_member_name(TypeID, 0);
     232         410 :         const bool bIsReal =
     233         410 :             pszName1 && (pszName1[0] == 'r' || pszName1[0] == 'R');
     234         410 :         H5free_memory(pszName1);
     235             : 
     236         410 :         char *pszName2 = H5Tget_member_name(TypeID, 1);
     237         410 :         const bool bIsImaginary =
     238         410 :             pszName2 && (pszName2[0] == 'i' || pszName2[0] == 'I');
     239         410 :         H5free_memory(pszName2);
     240             : 
     241         410 :         if (!bIsReal || !bIsImaginary)
     242             :         {
     243         171 :             H5Tclose(ElemTypeID);
     244         171 :             return GDT_Unknown;
     245             :         }
     246             : 
     247             :         // Check the native types to determine CInt16, CFloat32 or CFloat64
     248         239 :         GDALDataType eDataType = GDT_Unknown;
     249             : 
     250         239 :         if (H5Tequal(H5T_NATIVE_SHORT, ElemTypeID))
     251          58 :             eDataType = GDT_CInt16;
     252         181 :         else if (H5Tequal(H5T_NATIVE_INT, ElemTypeID))
     253          55 :             eDataType = GDT_CInt32;
     254         126 :         else if (H5Tequal(H5T_NATIVE_LONG, ElemTypeID))
     255             :         {
     256             : #if SIZEOF_UNSIGNED_LONG == 4
     257             :             eDataType = GDT_CInt32;
     258             : #else
     259           0 :             eDataType = GDT_Unknown;
     260             : #endif
     261             :         }
     262             : #ifdef HDF5_HAVE_FLOAT16
     263             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, ElemTypeID))
     264             :             eDataType = GDT_CFloat32;
     265             : #endif
     266         126 :         else if (H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID))
     267          66 :             eDataType = GDT_CFloat32;
     268          60 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID))
     269          60 :             eDataType = GDT_CFloat64;
     270             : 
     271             :         // Close the data type
     272         239 :         H5Tclose(ElemTypeID);
     273             : 
     274         239 :         return eDataType;
     275             :     }
     276             : 
     277       48355 :     return GDT_Unknown;
     278             : }
     279             : 
     280             : /************************************************************************/
     281             : /*                          IsNativeCFloat16()                          */
     282             : /************************************************************************/
     283             : 
     284           0 : /* static*/ bool HDF5Dataset::IsNativeCFloat16(hid_t hDataType)
     285             : {
     286             : #ifdef HDF5_HAVE_FLOAT16
     287             :     // For complex the compound type must contain 2 elements
     288             :     if (H5Tget_class(hDataType) != H5T_COMPOUND ||
     289             :         H5Tget_nmembers(hDataType) != 2)
     290             :         return false;
     291             : 
     292             :     // For complex the native types of both elements should be the same
     293             :     hid_t ElemTypeID = H5Tget_member_type(hDataType, 0);
     294             :     hid_t Elem2TypeID = H5Tget_member_type(hDataType, 1);
     295             :     const bool bRet = H5Tequal(ElemTypeID, H5T_NATIVE_FLOAT16) > 0 &&
     296             :                       H5Tequal(Elem2TypeID, H5T_NATIVE_FLOAT16) > 0;
     297             :     H5Tclose(ElemTypeID);
     298             :     H5Tclose(Elem2TypeID);
     299             :     return bRet;
     300             : #else
     301           0 :     CPL_IGNORE_RET_VAL(hDataType);
     302           0 :     return false;
     303             : #endif
     304             : }
     305             : 
     306             : /************************************************************************/
     307             : /*                          GetDataTypeName()                           */
     308             : /*                                                                      */
     309             : /*      Return the human readable name of data type                     */
     310             : /************************************************************************/
     311         180 : const char *HDF5Dataset::GetDataTypeName(hid_t TypeID)
     312             : {
     313             :     // Check for native types first
     314         180 :     if (H5Tget_class(TypeID) != H5T_COMPOUND)
     315             :     {
     316         168 :         if (H5Tequal(H5T_NATIVE_CHAR, TypeID))
     317           0 :             return "8-bit character";
     318         168 :         else if (H5Tequal(H5T_NATIVE_SCHAR, TypeID))
     319           0 :             return "8-bit signed character";
     320         168 :         else if (H5Tequal(H5T_NATIVE_UCHAR, TypeID))
     321          28 :             return "8-bit unsigned character";
     322         140 :         else if (H5Tequal(H5T_NATIVE_SHORT, TypeID))
     323           0 :             return "16-bit integer";
     324         140 :         else if (H5Tequal(H5T_NATIVE_USHORT, TypeID))
     325           9 :             return "16-bit unsigned integer";
     326         131 :         else if (H5Tequal(H5T_NATIVE_INT, TypeID))
     327          88 :             return "32-bit integer";
     328          43 :         else if (H5Tequal(H5T_NATIVE_UINT, TypeID))
     329           4 :             return "32-bit unsigned integer";
     330          39 :         else if (H5Tequal(H5T_NATIVE_INT64, TypeID))
     331           4 :             return "64-bit integer";
     332          35 :         else if (H5Tequal(H5T_NATIVE_UINT64, TypeID))
     333           4 :             return "64-bit unsigned integer";
     334          31 :         else if (H5Tequal(H5T_NATIVE_LONG, TypeID))
     335           0 :             return "32/64-bit integer";
     336          31 :         else if (H5Tequal(H5T_NATIVE_ULONG, TypeID))
     337           0 :             return "32/64-bit unsigned integer";
     338             : #ifdef HDF5_HAVE_FLOAT16
     339             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, TypeID))
     340             :             return "16-bit floating-point";
     341             : #endif
     342          31 :         else if (H5Tequal(H5T_NATIVE_FLOAT, TypeID))
     343          15 :             return "32-bit floating-point";
     344          16 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID))
     345           0 :             return "64-bit floating-point";
     346          16 :         else if (H5Tequal(H5T_NATIVE_LLONG, TypeID))
     347           0 :             return "64-bit integer";
     348          16 :         else if (H5Tequal(H5T_NATIVE_ULLONG, TypeID))
     349           0 :             return "64-bit unsigned integer";
     350          16 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID))
     351           0 :             return "64-bit floating-point";
     352             :     }
     353             :     else
     354             :     {
     355             :         // For complex the compound type must contain 2 elements
     356          12 :         if (H5Tget_nmembers(TypeID) != 2)
     357           2 :             return "Unknown";
     358             : 
     359             :         // For complex the native types of both elements should be the same
     360          10 :         hid_t ElemTypeID = H5Tget_member_type(TypeID, 0);
     361          10 :         hid_t Elem2TypeID = H5Tget_member_type(TypeID, 1);
     362          10 :         const bool bTypeEqual = H5Tequal(ElemTypeID, Elem2TypeID) > 0;
     363          10 :         H5Tclose(Elem2TypeID);
     364          10 :         if (!bTypeEqual)
     365             :         {
     366           0 :             H5Tclose(ElemTypeID);
     367           0 :             return "Unknown";
     368             :         }
     369             : 
     370             :         // Check the native types to determine CInt16, CFloat32 or CFloat64
     371          10 :         if (H5Tequal(H5T_NATIVE_SHORT, ElemTypeID))
     372             :         {
     373           0 :             H5Tclose(ElemTypeID);
     374           0 :             return "complex, 16-bit integer";
     375             :         }
     376          10 :         else if (H5Tequal(H5T_NATIVE_INT, ElemTypeID))
     377             :         {
     378           0 :             H5Tclose(ElemTypeID);
     379           0 :             return "complex, 32-bit integer";
     380             :         }
     381          10 :         else if (H5Tequal(H5T_NATIVE_LONG, ElemTypeID))
     382             :         {
     383           0 :             H5Tclose(ElemTypeID);
     384           0 :             return "complex, 32/64-bit integer";
     385             :         }
     386             : #ifdef HDF5_HAVE_FLOAT16
     387             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, ElemTypeID))
     388             :         {
     389             :             H5Tclose(ElemTypeID);
     390             :             return "complex, 16-bit floating-point";
     391             :         }
     392             : #endif
     393          10 :         else if (H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID))
     394             :         {
     395           7 :             H5Tclose(ElemTypeID);
     396           7 :             return "complex, 32-bit floating-point";
     397             :         }
     398           3 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID))
     399             :         {
     400           3 :             H5Tclose(ElemTypeID);
     401           3 :             return "complex, 64-bit floating-point";
     402             :         }
     403             :     }
     404             : 
     405          16 :     return "Unknown";
     406             : }
     407             : 
     408             : /************************************************************************/
     409             : /*                           GDAL_HDF5Open()                            */
     410             : /************************************************************************/
     411         173 : hid_t GDAL_HDF5Open(const std::string &osFilename)
     412             : {
     413             :     hid_t hHDF5;
     414             :     // Heuristics to able datasets split over several files, using the 'family'
     415             :     // driver. If passed the first file, and it contains a single 0, or
     416             :     // ends up with 0.h5 or 0.hdf5, replace the 0 with %d and try the family
     417             :     // driver.
     418         339 :     if (std::count(osFilename.begin(), osFilename.end(), '0') == 1 ||
     419         339 :         osFilename.find("0.h5") != std::string::npos ||
     420         165 :         osFilename.find("0.hdf5") != std::string::npos)
     421             :     {
     422           8 :         const auto zero_pos = osFilename.rfind('0');
     423          16 :         const auto osNewName = osFilename.substr(0, zero_pos) + "%d" +
     424          16 :                                osFilename.substr(zero_pos + 1);
     425           8 :         hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
     426           8 :         H5Pset_fapl_family(fapl, H5F_FAMILY_DEFAULT, H5P_DEFAULT);
     427             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
     428             : #pragma GCC diagnostic push
     429             : #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
     430             : #endif
     431           8 :         H5E_BEGIN_TRY
     432             :         {
     433           8 :             hHDF5 = H5Fopen(osNewName.c_str(), H5F_ACC_RDONLY, fapl);
     434             :         }
     435           8 :         H5E_END_TRY;
     436             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
     437             : #pragma GCC diagnostic pop
     438             : #endif
     439           8 :         H5Pclose(fapl);
     440           8 :         if (hHDF5 >= 0)
     441             :         {
     442           4 :             CPLDebug("HDF5", "Actually opening %s with 'family' driver",
     443             :                      osNewName.c_str());
     444           4 :             return hHDF5;
     445             :         }
     446             :     }
     447             : 
     448         169 :     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
     449         169 :     H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
     450         169 :     hHDF5 = H5Fopen(osFilename.c_str(), H5F_ACC_RDONLY, fapl);
     451         169 :     H5Pclose(fapl);
     452         169 :     return hHDF5;
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                                Open()                                */
     457             : /************************************************************************/
     458         106 : GDALDataset *HDF5Dataset::Open(GDALOpenInfo *poOpenInfo)
     459             : {
     460         106 :     if (!HDF5DatasetIdentify(poOpenInfo))
     461           0 :         return nullptr;
     462             : 
     463             :     HDF5_GLOBAL_LOCK();
     464             : 
     465         106 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
     466             :     {
     467          30 :         return OpenMultiDim(poOpenInfo);
     468             :     }
     469             : 
     470             :     // Create datasource.
     471          76 :     HDF5Dataset *const poDS = new HDF5Dataset();
     472             : 
     473          76 :     poDS->SetDescription(poOpenInfo->pszFilename);
     474             : 
     475             :     // Try opening the dataset.
     476          76 :     poDS->m_hHDF5 = GDAL_HDF5Open(poOpenInfo->pszFilename);
     477          76 :     if (poDS->m_hHDF5 < 0)
     478             :     {
     479           0 :         delete poDS;
     480           0 :         return nullptr;
     481             :     }
     482             : 
     483          76 :     poDS->hGroupID = H5Gopen(poDS->m_hHDF5, "/");
     484          76 :     if (poDS->hGroupID < 0)
     485             :     {
     486           0 :         delete poDS;
     487           0 :         return nullptr;
     488             :     }
     489             : 
     490          76 :     if (HDF5EOSParser::HasHDFEOS(poDS->hGroupID))
     491             :     {
     492           7 :         if (poDS->m_oHDFEOSParser.Parse(poDS->hGroupID))
     493             :         {
     494           7 :             CPLDebug("HDF5", "Successfully parsed HDFEOS metadata");
     495             :         }
     496             :     }
     497             : 
     498          76 :     poDS->ReadGlobalAttributes(true);
     499             : 
     500          76 :     if (STARTS_WITH(poDS->m_aosMetadata.FetchNameValueDef("mission_name", ""),
     501           0 :                     "Sentinel 3") &&
     502           0 :         EQUAL(
     503             :             poDS->m_aosMetadata.FetchNameValueDef("altimeter_sensor_name", ""),
     504           0 :             "SRAL") &&
     505           0 :         EQUAL(
     506             :             poDS->m_aosMetadata.FetchNameValueDef("radiometer_sensor_name", ""),
     507          76 :             "MWR") &&
     508           0 :         GDALGetDriverByName("netCDF") != nullptr)
     509             :     {
     510           0 :         delete poDS;
     511           0 :         return nullptr;
     512             :     }
     513             : 
     514             :     // Safety belt if S102Dataset::Identify() failed
     515          76 :     if (STARTS_WITH(
     516             :             poDS->m_aosMetadata.FetchNameValueDef("productSpecification", ""),
     517          77 :             "INT.IHO.S-102.") &&
     518           1 :         GDALGetDriverByName("S102") != nullptr)
     519             :     {
     520           1 :         delete poDS;
     521           2 :         std::string osS102Filename("S102:\"");
     522             :         osS102Filename +=
     523           1 :             CPLString(poOpenInfo->pszFilename).replaceAll("\"", "\\\"");
     524           1 :         osS102Filename += '"';
     525           1 :         return GDALDataset::Open(osS102Filename.c_str(), GDAL_OF_RASTER);
     526             :     }
     527             : 
     528             :     // Safety belt if S104Dataset::Identify() failed
     529          75 :     if (STARTS_WITH(
     530             :             poDS->m_aosMetadata.FetchNameValueDef("productSpecification", ""),
     531          77 :             "INT.IHO.S-104.") &&
     532           2 :         GDALGetDriverByName("S104") != nullptr)
     533             :     {
     534           2 :         delete poDS;
     535           4 :         std::string osS104Filename("S104:\"");
     536             :         osS104Filename +=
     537           2 :             CPLString(poOpenInfo->pszFilename).replaceAll("\"", "\\\"");
     538           2 :         osS104Filename += '"';
     539           2 :         return GDALDataset::Open(osS104Filename.c_str(), GDAL_OF_RASTER);
     540             :     }
     541             : 
     542             :     // Safety belt if S111Dataset::Identify() failed
     543          73 :     if (STARTS_WITH(
     544             :             poDS->m_aosMetadata.FetchNameValueDef("productSpecification", ""),
     545          74 :             "INT.IHO.S-111.") &&
     546           1 :         GDALGetDriverByName("S111") != nullptr)
     547             :     {
     548           1 :         delete poDS;
     549           2 :         std::string osS111Filename("S111:\"");
     550             :         osS111Filename +=
     551           1 :             CPLString(poOpenInfo->pszFilename).replaceAll("\"", "\\\"");
     552           1 :         osS111Filename += '"';
     553           1 :         return GDALDataset::Open(osS111Filename.c_str(), GDAL_OF_RASTER);
     554             :     }
     555             : 
     556          72 :     poDS->SetMetadata(poDS->m_aosMetadata.List());
     557             : 
     558          72 :     if (CSLCount(poDS->papszSubDatasets) / 2 >= 1)
     559          56 :         poDS->SetMetadata(poDS->papszSubDatasets, "SUBDATASETS");
     560             : 
     561             :     // Make sure we don't try to do any pam stuff with this dataset.
     562          72 :     poDS->nPamFlags |= GPF_NOSAVE;
     563             : 
     564             :     // If we have single subdataset only, open it immediately.
     565          72 :     int nSubDatasets = CSLCount(poDS->papszSubDatasets) / 2;
     566          72 :     if (nSubDatasets == 1)
     567             :     {
     568             :         CPLString osDSName =
     569          62 :             CSLFetchNameValue(poDS->papszSubDatasets, "SUBDATASET_1_NAME");
     570          31 :         delete poDS;
     571          31 :         return GDALDataset::Open(osDSName, poOpenInfo->nOpenFlags, nullptr,
     572          62 :                                  poOpenInfo->papszOpenOptions, nullptr);
     573             :     }
     574             :     else
     575             :     {
     576             :         // Confirm the requested access is supported.
     577          41 :         if (poOpenInfo->eAccess == GA_Update)
     578             :         {
     579           0 :             delete poDS;
     580           0 :             ReportUpdateNotSupportedByDriver("HDF5");
     581           0 :             return nullptr;
     582             :         }
     583             :     }
     584          41 :     return poDS;
     585             : }
     586             : 
     587             : /************************************************************************/
     588             : /*                          DestroyH5Objects()                          */
     589             : /*                                                                      */
     590             : /*      Erase all objects                                               */
     591             : /************************************************************************/
     592         886 : void HDF5Dataset::DestroyH5Objects(HDF5GroupObjects *poH5Object)
     593             : {
     594             :     // Visit all objects.
     595        1633 :     for (unsigned i = 0; i < poH5Object->nbObjs; i++)
     596         747 :         DestroyH5Objects(poH5Object->poHchild + i);
     597             : 
     598         886 :     if (poH5Object->poHparent == nullptr)
     599         146 :         return;
     600             : 
     601             :     // Erase some data.
     602         740 :     CPLFree(poH5Object->paDims);
     603         740 :     poH5Object->paDims = nullptr;
     604             : 
     605         740 :     CPLFree(poH5Object->pszPath);
     606         740 :     poH5Object->pszPath = nullptr;
     607             : 
     608         740 :     CPLFree(poH5Object->pszName);
     609         740 :     poH5Object->pszName = nullptr;
     610             : 
     611         740 :     CPLFree(poH5Object->pszUnderscorePath);
     612         740 :     poH5Object->pszUnderscorePath = nullptr;
     613             : 
     614         740 :     if (poH5Object->native > 0)
     615         446 :         H5Tclose(poH5Object->native);
     616         740 :     poH5Object->native = 0;
     617             : 
     618             :     // All Children are visited and can be deleted.
     619         740 :     if (poH5Object->nbObjs != 0)
     620             :     {
     621         241 :         CPLFree(poH5Object->poHchild);
     622         241 :         poH5Object->poHchild = nullptr;
     623             :     }
     624             : }
     625             : 
     626             : /************************************************************************/
     627             : /*                             CreatePath()                             */
     628             : /*                                                                      */
     629             : /*      Find Dataset path for HDopen                                    */
     630             : /************************************************************************/
     631        2415 : static void CreatePath(HDF5GroupObjects *poH5Object)
     632             : {
     633             :     // Recurse to the root path.
     634        4830 :     CPLString osPath;
     635        2415 :     if (poH5Object->poHparent != nullptr)
     636             :     {
     637        1675 :         CreatePath(poH5Object->poHparent);
     638        1675 :         osPath = poH5Object->poHparent->pszPath;
     639             :     }
     640             : 
     641             :     // Add name to the path.
     642        2415 :     if (!EQUAL(poH5Object->pszName, "/"))
     643             :     {
     644        1675 :         osPath.append("/");
     645        1675 :         osPath.append(poH5Object->pszName);
     646             :     }
     647             : 
     648             :     // Fill up path for each object.
     649        4830 :     CPLString osUnderscoreSpaceInName;
     650        2415 :     if (poH5Object->pszPath == nullptr)
     651             :     {
     652             :         // This is completely useless but needed if we want to keep
     653             :         // subdataset names as they have "always" been formatted,
     654             :         // with double slash at the beginning
     655         865 :         if (osPath.empty())
     656         125 :             osPath = "/";
     657             : 
     658             :         // Change space for underscore.
     659             :         char **papszPath =
     660         865 :             CSLTokenizeString2(osPath.c_str(), " ", CSLT_HONOURSTRINGS);
     661             : 
     662        2091 :         for (int i = 0; papszPath[i] != nullptr; i++)
     663             :         {
     664        1226 :             if (i > 0)
     665         361 :                 osUnderscoreSpaceInName.append("_");
     666        1226 :             osUnderscoreSpaceInName.append(papszPath[i]);
     667             :         }
     668         865 :         CSLDestroy(papszPath);
     669             : 
     670             :         // -1 to give room for NUL in C strings.
     671         865 :         constexpr size_t MAX_PATH = 8192 - 1;
     672             :         // TODO(schwehr): Is it an issue if the results are longer than 8192?
     673             :         // It appears that the output can never be longer than the source.
     674         865 :         if (osUnderscoreSpaceInName.size() > MAX_PATH)
     675           0 :             CPLError(CE_Fatal, CPLE_AppDefined,
     676             :                      "osUnderscoreSpaceInName longer than MAX_PATH: "
     677             :                      "%u > %u",
     678           0 :                      static_cast<unsigned int>(osUnderscoreSpaceInName.size()),
     679             :                      static_cast<unsigned int>(MAX_PATH));
     680         865 :         if (osPath.size() > MAX_PATH)
     681           0 :             CPLError(CE_Fatal, CPLE_AppDefined,
     682             :                      "osPath longer than MAX_PATH: %u > %u",
     683           0 :                      static_cast<unsigned int>(osPath.size()),
     684             :                      static_cast<unsigned int>(MAX_PATH));
     685             : 
     686         865 :         poH5Object->pszUnderscorePath =
     687         865 :             CPLStrdup(osUnderscoreSpaceInName.c_str());
     688         865 :         poH5Object->pszPath = CPLStrdup(osPath.c_str());
     689             :     }
     690        2415 : }
     691             : 
     692             : /************************************************************************/
     693             : /*                      HDF5GroupCheckDuplicate()                       */
     694             : /*                                                                      */
     695             : /*      Returns TRUE if an ancestor has the same objno[] as passed      */
     696             : /*      in - used to avoid looping in files with "links up" #(3218).    */
     697             : /************************************************************************/
     698             : 
     699         916 : static int HDF5GroupCheckDuplicate(HDF5GroupObjects *poHparent,
     700             :                                    unsigned long *objno)
     701             : 
     702             : {
     703         916 :     while (poHparent != nullptr)
     704             :     {
     705         628 :         if (poHparent->objno[0] == objno[0] && poHparent->objno[1] == objno[1])
     706           2 :             return TRUE;
     707             : 
     708         626 :         poHparent = poHparent->poHparent;
     709             :     }
     710             : 
     711         288 :     return FALSE;
     712             : }
     713             : 
     714             : /************************************************************************/
     715             : /*                      HDF5CreateGroupObjs()                           */
     716             : /*                                                                      */
     717             : /*      Create HDF5 hierarchy into a linked list                        */
     718             : /************************************************************************/
     719         740 : herr_t HDF5CreateGroupObjs(hid_t hHDF5, const char *pszObjName,
     720             :                            void *poHObjParent)
     721             : {
     722         740 :     HDF5GroupObjects *const poHparent =
     723             :         static_cast<HDF5GroupObjects *>(poHObjParent);
     724         740 :     HDF5GroupObjects *poHchild = poHparent->poHchild;
     725             :     H5G_stat_t oStatbuf;
     726             : 
     727         740 :     if (H5Gget_objinfo(hHDF5, pszObjName, FALSE, &oStatbuf) < 0)
     728           0 :         return -1;
     729             : 
     730             :     // Look for next child.
     731         740 :     unsigned idx = 0;  // idx is used after the for loop.
     732        1486 :     for (; idx < poHparent->nbObjs; idx++)
     733             :     {
     734        1486 :         if (poHchild->pszName == nullptr)
     735         740 :             break;
     736         746 :         poHchild++;
     737             :     }
     738             : 
     739         740 :     if (idx == poHparent->nbObjs)
     740           0 :         return -1;  // All children parsed.
     741             : 
     742             :     // Save child information.
     743         740 :     poHchild->pszName = CPLStrdup(pszObjName);
     744             : 
     745         740 :     poHchild->nType = oStatbuf.type;
     746         740 :     poHchild->nIndex = idx;
     747         740 :     poHchild->poHparent = poHparent;
     748         740 :     poHchild->nRank = 0;
     749         740 :     poHchild->paDims = nullptr;
     750         740 :     poHchild->HDatatype = 0;
     751         740 :     poHchild->objno[0] = oStatbuf.objno[0];
     752         740 :     poHchild->objno[1] = oStatbuf.objno[1];
     753         740 :     if (poHchild->pszPath == nullptr)
     754             :     {
     755         740 :         CreatePath(poHchild);
     756             :     }
     757         740 :     if (poHparent->pszPath == nullptr)
     758             :     {
     759           0 :         CreatePath(poHparent);
     760             :     }
     761             : 
     762         740 :     switch (oStatbuf.type)
     763             :     {
     764           3 :         case H5G_LINK:
     765             :         {
     766           3 :             poHchild->nbAttrs = 0;
     767           3 :             poHchild->nbObjs = 0;
     768           3 :             poHchild->poHchild = nullptr;
     769           3 :             poHchild->nRank = 0;
     770           3 :             poHchild->paDims = nullptr;
     771           3 :             poHchild->HDatatype = 0;
     772           3 :             break;
     773             :         }
     774         290 :         case H5G_GROUP:
     775             :         {
     776         290 :             hid_t hGroupID = H5I_INVALID_HID;  // Identifier of group.
     777         290 :             if ((hGroupID = H5Gopen(hHDF5, pszObjName)) == -1)
     778             :             {
     779           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     780             :                          "unable to access \"%s\" group.", pszObjName);
     781           0 :                 return -1;
     782             :             }
     783             :             // Number of attributes in object.
     784         290 :             const int nbAttrs = H5Aget_num_attrs(hGroupID);
     785         290 :             hsize_t nbObjs = 0;  // Number of objects in a group.
     786         290 :             H5Gget_num_objs(hGroupID, &nbObjs);
     787         290 :             poHchild->nbAttrs = nbAttrs;
     788         290 :             poHchild->nbObjs = static_cast<int>(nbObjs);
     789         290 :             poHchild->nRank = 0;
     790         290 :             poHchild->paDims = nullptr;
     791         290 :             poHchild->HDatatype = 0;
     792             : 
     793         290 :             if (nbObjs > 0)
     794             :             {
     795         241 :                 poHchild->poHchild = static_cast<HDF5GroupObjects *>(CPLCalloc(
     796             :                     static_cast<int>(nbObjs), sizeof(HDF5GroupObjects)));
     797         241 :                 memset(poHchild->poHchild, 0,
     798         241 :                        static_cast<size_t>(sizeof(HDF5GroupObjects) * nbObjs));
     799             :             }
     800             :             else
     801             :             {
     802          49 :                 poHchild->poHchild = nullptr;
     803             :             }
     804             : 
     805         290 :             if (!HDF5GroupCheckDuplicate(poHparent, oStatbuf.objno))
     806         288 :                 H5Giterate(hHDF5, pszObjName, nullptr, HDF5CreateGroupObjs,
     807             :                            poHchild);
     808             :             else
     809           2 :                 CPLDebug("HDF5", "avoiding link looping on node '%s'.",
     810             :                          pszObjName);
     811             : 
     812         290 :             H5Gclose(hGroupID);
     813         290 :             break;
     814             :         }
     815         446 :         case H5G_DATASET:
     816             :         {
     817         446 :             hid_t hDatasetID = H5I_INVALID_HID;  // Identifier of dataset.
     818         446 :             if ((hDatasetID = H5Dopen(hHDF5, pszObjName)) == -1)
     819             :             {
     820           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     821             :                          "unable to access \"%s\" dataset.", pszObjName);
     822           0 :                 return -1;
     823             :             }
     824         446 :             const int nbAttrs = H5Aget_num_attrs(hDatasetID);
     825         446 :             const hid_t datatype = H5Dget_type(hDatasetID);
     826         446 :             const hid_t dataspace = H5Dget_space(hDatasetID);
     827         446 :             const int n_dims = H5Sget_simple_extent_ndims(dataspace);
     828         446 :             const hid_t native = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
     829         446 :             hsize_t *maxdims = nullptr;
     830         446 :             hsize_t *dims = nullptr;
     831             : 
     832         446 :             if (n_dims > 0)
     833             :             {
     834             :                 dims =
     835         396 :                     static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t)));
     836             :                 maxdims =
     837         396 :                     static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t)));
     838             :             }
     839         446 :             H5Sget_simple_extent_dims(dataspace, dims, maxdims);
     840         446 :             if (maxdims != nullptr)
     841         396 :                 CPLFree(maxdims);
     842             : 
     843         446 :             if (n_dims > 0)
     844             :             {
     845         396 :                 poHchild->nRank = n_dims;        // rank of the array
     846         396 :                 poHchild->paDims = dims;         // dimension of the array.
     847         396 :                 poHchild->HDatatype = datatype;  // HDF5 datatype
     848             :             }
     849             :             else
     850             :             {
     851          50 :                 poHchild->nRank = -1;
     852          50 :                 poHchild->paDims = nullptr;
     853          50 :                 poHchild->HDatatype = 0;
     854             :             }
     855         446 :             poHchild->nbAttrs = nbAttrs;
     856         446 :             poHchild->nbObjs = 0;
     857         446 :             poHchild->poHchild = nullptr;
     858         446 :             poHchild->native = native;
     859         446 :             H5Tclose(datatype);
     860         446 :             H5Sclose(dataspace);
     861         446 :             H5Dclose(hDatasetID);
     862         446 :             break;
     863             :         }
     864           0 :         case H5G_TYPE:
     865             :         {
     866           0 :             poHchild->nbAttrs = 0;
     867           0 :             poHchild->nbObjs = 0;
     868           0 :             poHchild->poHchild = nullptr;
     869           0 :             poHchild->nRank = 0;
     870           0 :             poHchild->paDims = nullptr;
     871           0 :             poHchild->HDatatype = 0;
     872           0 :             break;
     873             :         }
     874           1 :         default:
     875           1 :             break;
     876             :     }
     877             : 
     878         740 :     return 0;
     879             : }
     880             : 
     881             : /************************************************************************/
     882             : /*                   HDF5DatasetCreateMetadataContext                   */
     883             : /************************************************************************/
     884             : 
     885             : struct HDF5DatasetCreateMetadataContext
     886             : {
     887             :     std::string m_osKey{};
     888             :     CPLStringList &m_aosMetadata;
     889             : 
     890             :     // Work variables
     891             :     std::string m_osValue{};
     892             : 
     893         688 :     explicit HDF5DatasetCreateMetadataContext(CPLStringList &aosMetadata)
     894         688 :         : m_aosMetadata(aosMetadata)
     895             :     {
     896         688 :     }
     897             : };
     898             : 
     899             : /************************************************************************/
     900             : /*                          HDF5AttrIterate()                           */
     901             : /************************************************************************/
     902             : 
     903        1453 : static herr_t HDF5AttrIterate(hid_t hH5ObjID, const char *pszAttrName,
     904             :                               void *pContext)
     905             : {
     906        1453 :     HDF5DatasetCreateMetadataContext *const psContext =
     907             :         static_cast<HDF5DatasetCreateMetadataContext *>(pContext);
     908             : 
     909        1453 :     psContext->m_osValue.clear();
     910             : 
     911        2906 :     std::string osKey(psContext->m_osKey);
     912             :     // Convert whitespaces into "_" for the attribute name component
     913             :     const CPLStringList aosTokens(CSLTokenizeString2(
     914        2906 :         pszAttrName, " ", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
     915        3651 :     for (int i = 0; i < aosTokens.size(); ++i)
     916             :     {
     917        2198 :         if (!osKey.empty())
     918        1671 :             osKey += '_';
     919        2198 :         osKey += aosTokens[i];
     920             :     }
     921             : 
     922        1453 :     const hid_t hAttrID = H5Aopen_name(hH5ObjID, pszAttrName);
     923        1453 :     const hid_t hAttrTypeID = H5Aget_type(hAttrID);
     924             :     const hid_t hAttrNativeType =
     925        1453 :         H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
     926        1453 :     const hid_t hAttrSpace = H5Aget_space(hAttrID);
     927             : 
     928        1453 :     if (H5Tget_class(hAttrNativeType) == H5T_VLEN)
     929             :     {
     930          40 :         H5Sclose(hAttrSpace);
     931          40 :         H5Tclose(hAttrNativeType);
     932          40 :         H5Tclose(hAttrTypeID);
     933          40 :         H5Aclose(hAttrID);
     934          40 :         return 0;
     935             :     }
     936             : 
     937        1413 :     hsize_t nSize[64] = {};
     938             :     const unsigned int nAttrDims =
     939        1413 :         H5Sget_simple_extent_dims(hAttrSpace, nSize, nullptr);
     940             : 
     941        1413 :     size_t nAttrElmts = 1;
     942        2109 :     for (hsize_t i = 0; i < nAttrDims; i++)
     943             :     {
     944         696 :         if (nSize[i] > std::numeric_limits<size_t>::max() / nAttrElmts)
     945             :         {
     946           0 :             H5Sclose(hAttrSpace);
     947           0 :             H5Tclose(hAttrNativeType);
     948           0 :             H5Tclose(hAttrTypeID);
     949           0 :             H5Aclose(hAttrID);
     950           0 :             return 0;
     951             :         }
     952         696 :         nAttrElmts *= static_cast<size_t>(nSize[i]);
     953             :     }
     954             : 
     955        1413 :     if (H5Tget_class(hAttrNativeType) == H5T_STRING)
     956             :     {
     957         625 :         if (H5Tis_variable_str(hAttrNativeType))
     958             :         {
     959             :             char **papszStrings = static_cast<char **>(
     960          92 :                 VSI_MALLOC2_VERBOSE(nAttrElmts, sizeof(char *)));
     961          92 :             if (papszStrings)
     962             :             {
     963             :                 // Read the values.
     964          92 :                 H5Aread(hAttrID, hAttrNativeType, papszStrings);
     965             : 
     966             :                 // Concatenate all values as one string separated by a space.
     967             :                 psContext->m_osValue =
     968          92 :                     papszStrings[0] ? papszStrings[0] : "{NULL}";
     969         174 :                 for (hsize_t i = 1; i < nAttrElmts; i++)
     970             :                 {
     971          82 :                     psContext->m_osValue += " ";
     972             :                     psContext->m_osValue +=
     973          82 :                         papszStrings[i] ? papszStrings[i] : "{NULL}";
     974             :                 }
     975             : 
     976          92 :                 H5Dvlen_reclaim(hAttrNativeType, hAttrSpace, H5P_DEFAULT,
     977             :                                 papszStrings);
     978          92 :                 CPLFree(papszStrings);
     979             :             }
     980             :         }
     981             :         else
     982             :         {
     983         533 :             const hsize_t nAttrSize = H5Aget_storage_size(hAttrID);
     984         533 :             if (nAttrSize <= static_cast<hsize_t>(INT_MAX))
     985             :             {
     986             :                 try
     987             :                 {
     988         533 :                     psContext->m_osValue.resize(static_cast<size_t>(nAttrSize));
     989         533 :                     H5Aread(hAttrID, hAttrNativeType, &psContext->m_osValue[0]);
     990             :                 }
     991           0 :                 catch (const std::exception &)
     992             :                 {
     993             :                 }
     994             :             }
     995             :         }
     996             :     }
     997             :     else
     998             :     {
     999         788 :         constexpr size_t nDataLen = 32;
    1000             :         char szData[nDataLen];
    1001             : 
    1002         788 :         void *buf = nullptr;
    1003             : 
    1004         788 :         if (nAttrElmts > 0)
    1005             :         {
    1006         788 :             buf = VSI_MALLOC2_VERBOSE(nAttrElmts, H5Tget_size(hAttrNativeType));
    1007         788 :             if (buf)
    1008         788 :                 H5Aread(hAttrID, hAttrNativeType, buf);
    1009             :         }
    1010         788 :         const bool bIsSCHAR = H5Tequal(H5T_NATIVE_SCHAR, hAttrNativeType) > 0;
    1011         788 :         const bool bIsUCHAR = H5Tequal(H5T_NATIVE_UCHAR, hAttrNativeType) > 0;
    1012         799 :         if (buf && (bIsSCHAR || bIsUCHAR) &&
    1013          11 :             CPLTestBool(CPLGetConfigOption("GDAL_HDF5_CHAR_AS_STRING", "NO")))
    1014             :         {
    1015             :             // Compatibility mode with ancient GDAL versions where we consider
    1016             :             // array of SCHAR/UCHAR as strings. Likely inappropriate mode...
    1017           0 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1018             :             {
    1019           0 :                 snprintf(szData, nDataLen, "%c", static_cast<char *>(buf)[i]);
    1020           0 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1021             :                 {
    1022           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1023             :                              "Header data too long. Truncated");
    1024           0 :                     break;
    1025             :                 }
    1026           0 :                 psContext->m_osValue += szData;
    1027             :             }
    1028             :         }
    1029         788 :         else if (buf && bIsSCHAR)
    1030             :         {
    1031           4 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1032             :             {
    1033           2 :                 snprintf(szData, nDataLen, "%d",
    1034           2 :                          static_cast<signed char *>(buf)[i]);
    1035           2 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1036             :                 {
    1037           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1038             :                              "Header data too long. Truncated");
    1039           0 :                     break;
    1040             :                 }
    1041           2 :                 if (i > 0)
    1042           0 :                     psContext->m_osValue += ' ';
    1043           2 :                 psContext->m_osValue += szData;
    1044           2 :             }
    1045             :         }
    1046         786 :         else if (buf && bIsUCHAR)
    1047             :         {
    1048          18 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1049             :             {
    1050           9 :                 snprintf(szData, nDataLen, "%u",
    1051           9 :                          static_cast<unsigned char *>(buf)[i]);
    1052           9 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1053             :                 {
    1054           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1055             :                              "Header data too long. Truncated");
    1056           0 :                     break;
    1057             :                 }
    1058           9 :                 if (i > 0)
    1059           0 :                     psContext->m_osValue += ' ';
    1060           9 :                 psContext->m_osValue += szData;
    1061           9 :             }
    1062             :         }
    1063         777 :         else if (buf && H5Tequal(H5T_NATIVE_SHORT, hAttrNativeType) > 0)
    1064             :         {
    1065          38 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1066             :             {
    1067          19 :                 snprintf(szData, nDataLen, "%d", static_cast<short *>(buf)[i]);
    1068          19 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1069             :                 {
    1070           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1071             :                              "Header data too long. Truncated");
    1072           0 :                     break;
    1073             :                 }
    1074          19 :                 if (i > 0)
    1075           0 :                     psContext->m_osValue += ' ';
    1076          19 :                 psContext->m_osValue += szData;
    1077             :             }
    1078             :         }
    1079         758 :         else if (buf && H5Tequal(H5T_NATIVE_USHORT, hAttrNativeType) > 0)
    1080             :         {
    1081          46 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1082             :             {
    1083          23 :                 snprintf(szData, nDataLen, "%u",
    1084          23 :                          static_cast<unsigned short *>(buf)[i]);
    1085          23 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1086             :                 {
    1087           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1088             :                              "Header data too long. Truncated");
    1089           0 :                     break;
    1090             :                 }
    1091          23 :                 if (i > 0)
    1092           0 :                     psContext->m_osValue += ' ';
    1093          23 :                 psContext->m_osValue += szData;
    1094             :             }
    1095             :         }
    1096         735 :         else if (buf && H5Tequal(H5T_NATIVE_INT, hAttrNativeType) > 0)
    1097             :         {
    1098         370 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1099             :             {
    1100         199 :                 snprintf(szData, nDataLen, "%d", static_cast<int *>(buf)[i]);
    1101         199 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1102             :                 {
    1103           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1104             :                              "Header data too long. Truncated");
    1105           0 :                     break;
    1106             :                 }
    1107         199 :                 if (i > 0)
    1108          28 :                     psContext->m_osValue += ' ';
    1109         199 :                 psContext->m_osValue += szData;
    1110             :             }
    1111             :         }
    1112         564 :         else if (buf && H5Tequal(H5T_NATIVE_UINT, hAttrNativeType) > 0)
    1113             :         {
    1114         216 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1115             :             {
    1116         108 :                 snprintf(szData, nDataLen, "%u",
    1117         108 :                          static_cast<unsigned int *>(buf)[i]);
    1118         108 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1119             :                 {
    1120           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1121             :                              "Header data too long. Truncated");
    1122           0 :                     break;
    1123             :                 }
    1124         108 :                 if (i > 0)
    1125           0 :                     psContext->m_osValue += ' ';
    1126         108 :                 psContext->m_osValue += szData;
    1127             :             }
    1128             :         }
    1129         456 :         else if (buf && H5Tequal(H5T_NATIVE_INT64, hAttrNativeType) > 0)
    1130             :         {
    1131         100 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1132             :             {
    1133          65 :                 snprintf(szData, nDataLen, CPL_FRMT_GIB,
    1134          65 :                          static_cast<GIntBig *>(buf)[i]);
    1135          65 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1136             :                 {
    1137           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1138             :                              "Header data too long. Truncated");
    1139           0 :                     break;
    1140             :                 }
    1141          65 :                 if (i > 0)
    1142          30 :                     psContext->m_osValue += ' ';
    1143          65 :                 psContext->m_osValue += szData;
    1144             :             }
    1145             :         }
    1146         421 :         else if (buf && H5Tequal(H5T_NATIVE_UINT64, hAttrNativeType) > 0)
    1147             :         {
    1148          10 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1149             :             {
    1150           5 :                 snprintf(szData, nDataLen, CPL_FRMT_GUIB,
    1151           5 :                          static_cast<GUIntBig *>(buf)[i]);
    1152           5 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1153             :                 {
    1154           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1155             :                              "Header data too long. Truncated");
    1156           0 :                     break;
    1157             :                 }
    1158           5 :                 if (i > 0)
    1159           0 :                     psContext->m_osValue += ' ';
    1160           5 :                 psContext->m_osValue += szData;
    1161             :             }
    1162             :         }
    1163         416 :         else if (buf && H5Tequal(H5T_NATIVE_LONG, hAttrNativeType) > 0)
    1164             :         {
    1165           0 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1166             :             {
    1167           0 :                 snprintf(szData, nDataLen, "%ld", static_cast<long *>(buf)[i]);
    1168           0 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1169             :                 {
    1170           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1171             :                              "Header data too long. Truncated");
    1172           0 :                     break;
    1173             :                 }
    1174           0 :                 if (i > 0)
    1175           0 :                     psContext->m_osValue += ' ';
    1176           0 :                 psContext->m_osValue += szData;
    1177             :             }
    1178             :         }
    1179         416 :         else if (buf && H5Tequal(H5T_NATIVE_ULONG, hAttrNativeType) > 0)
    1180             :         {
    1181           0 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1182             :             {
    1183           0 :                 snprintf(szData, nDataLen, "%lu",
    1184           0 :                          static_cast<unsigned long *>(buf)[i]);
    1185           0 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1186             :                 {
    1187           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1188             :                              "Header data too long. Truncated");
    1189           0 :                     break;
    1190             :                 }
    1191           0 :                 if (i > 0)
    1192           0 :                     psContext->m_osValue += ' ';
    1193           0 :                 psContext->m_osValue += szData;
    1194             :             }
    1195             :         }
    1196             : #ifdef HDF5_HAVE_FLOAT16
    1197             :         else if (buf && H5Tequal(H5T_NATIVE_FLOAT16, hAttrNativeType) > 0)
    1198             :         {
    1199             :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1200             :             {
    1201             :                 const uint16_t nVal16 = static_cast<uint16_t *>(buf)[i];
    1202             :                 const uint32_t nVal32 = CPLHalfToFloat(nVal16);
    1203             :                 float fVal;
    1204             :                 memcpy(&fVal, &nVal32, sizeof(fVal));
    1205             :                 CPLsnprintf(szData, nDataLen, "%.8g", fVal);
    1206             :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1207             :                 {
    1208             :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1209             :                              "Header data too long. Truncated");
    1210             :                     break;
    1211             :                 }
    1212             :                 if (i > 0)
    1213             :                     psContext->m_osValue += ' ';
    1214             :                 psContext->m_osValue += szData;
    1215             :             }
    1216             :         }
    1217             : #endif
    1218         416 :         else if (buf && H5Tequal(H5T_NATIVE_FLOAT, hAttrNativeType) > 0)
    1219             :         {
    1220         283 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1221             :             {
    1222         144 :                 CPLsnprintf(szData, nDataLen, "%.8g",
    1223         144 :                             static_cast<float *>(buf)[i]);
    1224         144 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1225             :                 {
    1226           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1227             :                              "Header data too long. Truncated");
    1228           0 :                     break;
    1229             :                 }
    1230         144 :                 if (i > 0)
    1231           5 :                     psContext->m_osValue += ' ';
    1232         144 :                 psContext->m_osValue += szData;
    1233             :             }
    1234             :         }
    1235         277 :         else if (buf && H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType) > 0)
    1236             :         {
    1237         602 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1238             :             {
    1239         380 :                 CPLsnprintf(szData, nDataLen, "%.15g",
    1240         380 :                             static_cast<double *>(buf)[i]);
    1241         380 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1242             :                 {
    1243           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1244             :                              "Header data too long. Truncated");
    1245           0 :                     break;
    1246             :                 }
    1247         380 :                 if (i > 0)
    1248         158 :                     psContext->m_osValue += ' ';
    1249         380 :                 psContext->m_osValue += szData;
    1250             :             }
    1251             :         }
    1252         788 :         CPLFree(buf);
    1253             :     }
    1254        1413 :     H5Sclose(hAttrSpace);
    1255        1413 :     H5Tclose(hAttrNativeType);
    1256        1413 :     H5Tclose(hAttrTypeID);
    1257        1413 :     H5Aclose(hAttrID);
    1258        1413 :     psContext->m_aosMetadata.SetNameValue(osKey.c_str(),
    1259        1413 :                                           psContext->m_osValue.c_str());
    1260             : 
    1261        1413 :     return 0;
    1262             : }
    1263             : 
    1264             : /************************************************************************/
    1265             : /*                           CreateMetadata()                           */
    1266             : /************************************************************************/
    1267         709 : CPLErr HDF5Dataset::CreateMetadata(hid_t hHDF5, HDF5GroupObjects *poH5Object,
    1268             :                                    int nType, bool bPrefixWithDatasetName,
    1269             :                                    CPLStringList &aosMetadata)
    1270             : {
    1271             : 
    1272         709 :     if (!poH5Object->pszPath)
    1273          21 :         return CE_None;
    1274             : 
    1275         688 :     if (EQUAL(poH5Object->pszPath, ""))
    1276           0 :         return CE_None;
    1277             : 
    1278         688 :     const int nbAttrs = poH5Object->nbAttrs;
    1279             : 
    1280         688 :     HDF5DatasetCreateMetadataContext sContext(aosMetadata);
    1281             : 
    1282         688 :     if (bPrefixWithDatasetName)
    1283             :     {
    1284             :         // Convert "/" into "_" for the path component
    1285         617 :         const char *pszPath = poH5Object->pszUnderscorePath;
    1286         617 :         if (pszPath != nullptr && strlen(pszPath) > 0)
    1287             :         {
    1288             :             const CPLStringList aosTokens(
    1289        1234 :                 CSLTokenizeString2(pszPath, "/", CSLT_HONOURSTRINGS));
    1290        1588 :             for (int i = 0; i < aosTokens.size(); ++i)
    1291             :             {
    1292         971 :                 if (i != 0)
    1293         479 :                     sContext.m_osKey += '_';
    1294         971 :                 sContext.m_osKey += aosTokens[i];
    1295             :             }
    1296             :         }
    1297             :     }
    1298             : 
    1299         688 :     switch (nType)
    1300             :     {
    1301         415 :         case H5G_GROUP:
    1302         415 :             if (nbAttrs > 0)
    1303             :             {
    1304             :                 // Identifier of group.
    1305         109 :                 const hid_t l_hGroupID = H5Gopen(hHDF5, poH5Object->pszPath);
    1306         109 :                 H5Aiterate(l_hGroupID, nullptr, HDF5AttrIterate, &sContext);
    1307         109 :                 H5Gclose(l_hGroupID);
    1308             :             }
    1309         415 :             break;
    1310         273 :         case H5G_DATASET:
    1311         273 :             if (nbAttrs > 0)
    1312             :             {
    1313         194 :                 const hid_t hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath);
    1314         194 :                 H5Aiterate(hDatasetID, nullptr, HDF5AttrIterate, &sContext);
    1315         194 :                 H5Dclose(hDatasetID);
    1316             :             }
    1317         273 :             break;
    1318             : 
    1319           0 :         default:
    1320           0 :             break;
    1321             :     }
    1322             : 
    1323         688 :     return CE_None;
    1324             : }
    1325             : 
    1326             : /************************************************************************/
    1327             : /*                       HDF5FindDatasetObjectsbyPath()                 */
    1328             : /*      Find object by name                                             */
    1329             : /************************************************************************/
    1330             : HDF5GroupObjects *
    1331         381 : HDF5Dataset::HDF5FindDatasetObjectsbyPath(HDF5GroupObjects *poH5Objects,
    1332             :                                           const char *pszDatasetPath)
    1333             : {
    1334         381 :     if (poH5Objects->nType == H5G_DATASET &&
    1335         149 :         EQUAL(poH5Objects->pszUnderscorePath, pszDatasetPath))
    1336             :     {
    1337             : 
    1338             : #ifdef DEBUG_VERBOSE
    1339             :         printf("found it! %p\n", poH5Objects); /*ok*/
    1340             : #endif
    1341          76 :         return poH5Objects;
    1342             :     }
    1343             : 
    1344         305 :     HDF5Dataset *const poDS = this;
    1345             : 
    1346         305 :     if (poH5Objects->nbObjs > 0)
    1347             :     {
    1348         336 :         for (unsigned int i = 0; i < poH5Objects->nbObjs; i++)
    1349             :         {
    1350             :             HDF5GroupObjects *poObjectsFound =
    1351         305 :                 poDS->HDF5FindDatasetObjectsbyPath(poH5Objects->poHchild + i,
    1352             :                                                    pszDatasetPath);
    1353             :             // Is this our dataset?
    1354         305 :             if (poObjectsFound != nullptr)
    1355         173 :                 return poObjectsFound;
    1356             :         }
    1357             :     }
    1358             :     // Dataset has not been found.
    1359         132 :     return nullptr;
    1360             : }
    1361             : 
    1362             : /************************************************************************/
    1363             : /*                       HDF5FindDatasetObjects()                       */
    1364             : /*      Find object by name                                             */
    1365             : /************************************************************************/
    1366             : HDF5GroupObjects *
    1367          32 : HDF5Dataset::HDF5FindDatasetObjects(HDF5GroupObjects *poH5Objects,
    1368             :                                     const char *pszDatasetName)
    1369             : {
    1370          32 :     if (poH5Objects->nType == H5G_DATASET &&
    1371          23 :         EQUAL(poH5Objects->pszName, pszDatasetName))
    1372             :     {
    1373             : 
    1374             : #ifdef DEBUG_VERBOSE
    1375             :         printf("found it! %p\n", poH5Objects); /*ok*/
    1376             : #endif
    1377           0 :         return poH5Objects;
    1378             :     }
    1379             : 
    1380          32 :     HDF5Dataset *poDS = this;
    1381             : 
    1382          32 :     if (poH5Objects->nbObjs > 0)
    1383             :     {
    1384          32 :         for (unsigned int i = 0; i < poH5Objects->nbObjs; i++)
    1385             :         {
    1386          48 :             HDF5GroupObjects *poObjectsFound = poDS->HDF5FindDatasetObjects(
    1387          24 :                 poH5Objects->poHchild + i, pszDatasetName);
    1388             :             // Is this our dataset?
    1389          24 :             if (poObjectsFound != nullptr)
    1390           0 :                 return poObjectsFound;
    1391             :         }
    1392             :     }
    1393             : 
    1394             :     // Dataset has not been found.
    1395          32 :     return nullptr;
    1396             : }
    1397             : 
    1398             : /************************************************************************/
    1399             : /*                        HDF5ListGroupObjects()                        */
    1400             : /*                                                                      */
    1401             : /*      List all objects in HDF5                                        */
    1402             : /************************************************************************/
    1403         886 : CPLErr HDF5Dataset::HDF5ListGroupObjects(HDF5GroupObjects *poRootGroup,
    1404             :                                          int bSUBDATASET)
    1405             : {
    1406         886 :     HDF5Dataset *poDS = this;
    1407             : 
    1408         886 :     if (poRootGroup->nbObjs > 0)
    1409        1113 :         for (hsize_t i = 0; i < poRootGroup->nbObjs; i++)
    1410             :         {
    1411         747 :             poDS->HDF5ListGroupObjects(poRootGroup->poHchild + i, bSUBDATASET);
    1412             :         }
    1413             : 
    1414         886 :     if (poRootGroup->nType == H5G_GROUP)
    1415             :     {
    1416         436 :         CreateMetadata(m_hHDF5, poRootGroup, H5G_GROUP, true, m_aosMetadata);
    1417             :     }
    1418             : 
    1419             :     // Create Sub dataset list.
    1420             : 
    1421        1114 :     if (poRootGroup->nType == H5G_DATASET && bSUBDATASET &&
    1422         228 :         poDS->GetDataType(poRootGroup->native) == GDT_Unknown)
    1423             :     {
    1424          26 :         if (!EQUAL(poRootGroup->pszUnderscorePath,
    1425             :                    "//HDFEOS_INFORMATION/StructMetadata.0"))
    1426             :         {
    1427          19 :             CPLDebug("HDF5", "Skipping unsupported %s of type %s",
    1428             :                      poRootGroup->pszUnderscorePath,
    1429             :                      poDS->GetDataTypeName(poRootGroup->native));
    1430             :         }
    1431             :     }
    1432         860 :     else if (poRootGroup->nType == H5G_DATASET && bSUBDATASET)
    1433             :     {
    1434         202 :         CreateMetadata(m_hHDF5, poRootGroup, H5G_DATASET, true, m_aosMetadata);
    1435             : 
    1436         571 :         for (int i = 0; i < poRootGroup->nRank; ++i)
    1437             :         {
    1438         742 :             if (poRootGroup->paDims[i] >
    1439         371 :                 static_cast<hsize_t>(std::numeric_limits<int>::max()))
    1440             :             {
    1441           2 :                 CPLDebug("HDF5",
    1442             :                          "Not reporting %s as subdataset as at least one of "
    1443             :                          "its dimension size exceeds INT_MAX!",
    1444             :                          poRootGroup->pszUnderscorePath);
    1445          41 :                 return CE_None;
    1446             :             }
    1447             :         }
    1448             : 
    1449         200 :         CPLString osStr;
    1450         200 :         switch (poRootGroup->nRank)
    1451             :         {
    1452         151 :             case 2:
    1453         151 :                 osStr.Printf("%dx%d", static_cast<int>(poRootGroup->paDims[0]),
    1454         151 :                              static_cast<int>(poRootGroup->paDims[1]));
    1455         151 :                 break;
    1456          10 :             case 3:
    1457             :                 osStr.Printf("%dx%dx%d",
    1458          10 :                              static_cast<int>(poRootGroup->paDims[0]),
    1459          10 :                              static_cast<int>(poRootGroup->paDims[1]),
    1460          10 :                              static_cast<int>(poRootGroup->paDims[2]));
    1461          10 :                 break;
    1462          39 :             default:
    1463          39 :                 return CE_None;
    1464             :         }
    1465             : 
    1466         322 :         HDF5EOSParser::GridMetadata oGridMetadata;
    1467         322 :         HDF5EOSParser::SwathFieldMetadata oSwathFieldMetadata;
    1468         334 :         if (m_oHDFEOSParser.GetDataModel() == HDF5EOSParser::DataModel::GRID &&
    1469         167 :             m_oHDFEOSParser.GetGridMetadata(poRootGroup->pszUnderscorePath,
    1470         167 :                                             oGridMetadata) &&
    1471           0 :             static_cast<int>(oGridMetadata.aoDimensions.size()) ==
    1472           0 :                 poRootGroup->nRank)
    1473             :         {
    1474           0 :             int nXDimSize = 0;
    1475           0 :             int nYDimSize = 0;
    1476           0 :             int nOtherDimSize = 0;
    1477           0 :             std::string osOtherDimName;
    1478           0 :             for (const auto &oDim : oGridMetadata.aoDimensions)
    1479             :             {
    1480           0 :                 if (oDim.osName == "XDim")
    1481           0 :                     nXDimSize = oDim.nSize;
    1482           0 :                 else if (oDim.osName == "YDim")
    1483           0 :                     nYDimSize = oDim.nSize;
    1484             :                 else
    1485             :                 {
    1486           0 :                     osOtherDimName = oDim.osName;
    1487           0 :                     nOtherDimSize = oDim.nSize;
    1488             :                 }
    1489             :             }
    1490           0 :             switch (poRootGroup->nRank)
    1491             :             {
    1492           0 :                 case 2:
    1493           0 :                     osStr.Printf("(y=%d)x(x=%d)", nYDimSize, nXDimSize);
    1494           0 :                     break;
    1495           0 :                 case 3:
    1496             :                 {
    1497           0 :                     if (osOtherDimName == oGridMetadata.aoDimensions[0].osName)
    1498             :                         osStr.Printf("(%s=%d)x(y=%d)x(x=%d)",
    1499             :                                      osOtherDimName.c_str(), nOtherDimSize,
    1500           0 :                                      nYDimSize, nXDimSize);
    1501             :                     else
    1502             :                         osStr.Printf("(y=%d)x(x=%d)x(%s=%d)", nYDimSize,
    1503             :                                      nXDimSize, osOtherDimName.c_str(),
    1504           0 :                                      nOtherDimSize);
    1505           0 :                     break;
    1506             :                 }
    1507           0 :                 default:
    1508           0 :                     break;
    1509             :             }
    1510             :         }
    1511         161 :         else if (m_oHDFEOSParser.GetDataModel() ==
    1512           3 :                      HDF5EOSParser::DataModel::SWATH &&
    1513           3 :                  m_oHDFEOSParser.GetSwathFieldMetadata(
    1514           3 :                      poRootGroup->pszUnderscorePath, oSwathFieldMetadata) &&
    1515           3 :                  static_cast<int>(oSwathFieldMetadata.aoDimensions.size()) ==
    1516           3 :                      poRootGroup->nRank &&
    1517         167 :                  oSwathFieldMetadata.iXDim >= 0 &&
    1518           3 :                  oSwathFieldMetadata.iYDim >= 0)
    1519             :         {
    1520             :             const std::string &osXDimName =
    1521           3 :                 oSwathFieldMetadata.aoDimensions[oSwathFieldMetadata.iXDim]
    1522           3 :                     .osName;
    1523             :             const int nXDimSize =
    1524           3 :                 oSwathFieldMetadata.aoDimensions[oSwathFieldMetadata.iXDim]
    1525           3 :                     .nSize;
    1526             :             const std::string &osYDimName =
    1527           3 :                 oSwathFieldMetadata.aoDimensions[oSwathFieldMetadata.iYDim]
    1528           3 :                     .osName;
    1529             :             const int nYDimSize =
    1530           3 :                 oSwathFieldMetadata.aoDimensions[oSwathFieldMetadata.iYDim]
    1531           3 :                     .nSize;
    1532           3 :             switch (poRootGroup->nRank)
    1533             :             {
    1534           2 :                 case 2:
    1535             :                     osStr.Printf("(%s=%d)x(%s=%d)", osYDimName.c_str(),
    1536           2 :                                  nYDimSize, osXDimName.c_str(), nXDimSize);
    1537           2 :                     break;
    1538           1 :                 case 3:
    1539             :                 {
    1540             :                     const std::string &osOtherDimName =
    1541             :                         oSwathFieldMetadata
    1542           1 :                             .aoDimensions[oSwathFieldMetadata.iOtherDim]
    1543           1 :                             .osName;
    1544             :                     const int nOtherDimSize =
    1545             :                         oSwathFieldMetadata
    1546           1 :                             .aoDimensions[oSwathFieldMetadata.iOtherDim]
    1547           1 :                             .nSize;
    1548           1 :                     if (oSwathFieldMetadata.iOtherDim == 0)
    1549             :                     {
    1550             :                         osStr.Printf("(%s=%d)x(%s=%d)x(%s=%d)",
    1551             :                                      osOtherDimName.c_str(), nOtherDimSize,
    1552             :                                      osYDimName.c_str(), nYDimSize,
    1553           1 :                                      osXDimName.c_str(), nXDimSize);
    1554             :                     }
    1555             :                     else
    1556             :                     {
    1557             :                         osStr.Printf("(%s=%d)x(%s=%d)x(%s=%d)",
    1558             :                                      osYDimName.c_str(), nYDimSize,
    1559             :                                      osXDimName.c_str(), nXDimSize,
    1560           0 :                                      osOtherDimName.c_str(), nOtherDimSize);
    1561             :                     }
    1562           1 :                     break;
    1563             :                 }
    1564           0 :                 default:
    1565           0 :                     break;
    1566             :             }
    1567             :         }
    1568             : 
    1569         161 :         const std::string osDim = osStr;
    1570             : 
    1571         161 :         osStr.Printf("SUBDATASET_%d_NAME", ++(poDS->nSubDataCount));
    1572             : 
    1573         161 :         poDS->papszSubDatasets =
    1574         161 :             CSLSetNameValue(poDS->papszSubDatasets, osStr.c_str(),
    1575         161 :                             CPLSPrintf("HDF5:\"%s\":%s", poDS->GetDescription(),
    1576             :                                        poRootGroup->pszUnderscorePath));
    1577             : 
    1578         161 :         osStr.Printf("SUBDATASET_%d_DESC", poDS->nSubDataCount);
    1579             : 
    1580         161 :         poDS->papszSubDatasets = CSLSetNameValue(
    1581             :             poDS->papszSubDatasets, osStr.c_str(),
    1582             :             CPLSPrintf("[%s] %s (%s)", osDim.c_str(),
    1583             :                        poRootGroup->pszUnderscorePath,
    1584             :                        poDS->GetDataTypeName(poRootGroup->native)));
    1585             :     }
    1586             : 
    1587         845 :     return CE_None;
    1588             : }
    1589             : 
    1590             : /************************************************************************/
    1591             : /*                        ReadGlobalAttributes()                        */
    1592             : /************************************************************************/
    1593         139 : CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET)
    1594             : {
    1595             :     HDF5GroupObjects *poRootGroup =
    1596         139 :         static_cast<HDF5GroupObjects *>(CPLCalloc(sizeof(HDF5GroupObjects), 1));
    1597             : 
    1598         139 :     poH5RootGroup = poRootGroup;
    1599         139 :     poRootGroup->pszName = CPLStrdup("/");
    1600         139 :     poRootGroup->nType = H5G_GROUP;
    1601         139 :     poRootGroup->poHparent = nullptr;
    1602         139 :     poRootGroup->pszPath = nullptr;
    1603         139 :     poRootGroup->pszUnderscorePath = nullptr;
    1604             : 
    1605         139 :     if (m_hHDF5 < 0)
    1606             :     {
    1607           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hHDF5 < 0!");
    1608           0 :         return CE_None;
    1609             :     }
    1610             : 
    1611         139 :     H5G_stat_t oStatbuf = {{0, 0}, {0, 0}, 0, H5G_UNKNOWN, 0, 0, {0, 0, 0, 0}};
    1612             : 
    1613         139 :     if (H5Gget_objinfo(m_hHDF5, "/", FALSE, &oStatbuf) < 0)
    1614           0 :         return CE_Failure;
    1615         139 :     poRootGroup->objno[0] = oStatbuf.objno[0];
    1616         139 :     poRootGroup->objno[1] = oStatbuf.objno[1];
    1617             : 
    1618         139 :     if (hGroupID > 0)
    1619         139 :         H5Gclose(hGroupID);
    1620         139 :     hGroupID = H5Gopen(m_hHDF5, "/");
    1621         139 :     if (hGroupID < 0)
    1622             :     {
    1623           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hGroupId <0!");
    1624           0 :         return CE_None;
    1625             :     }
    1626             : 
    1627         139 :     poRootGroup->nbAttrs = H5Aget_num_attrs(hGroupID);
    1628             : 
    1629         139 :     H5Gget_num_objs(hGroupID, &(poRootGroup->nbObjs));
    1630             : 
    1631         139 :     if (poRootGroup->nbObjs > 0)
    1632             :     {
    1633         125 :         poRootGroup->poHchild = static_cast<HDF5GroupObjects *>(
    1634         125 :             CPLCalloc(static_cast<size_t>(poRootGroup->nbObjs),
    1635             :                       sizeof(HDF5GroupObjects)));
    1636         125 :         H5Giterate(hGroupID, "/", nullptr, HDF5CreateGroupObjs, poRootGroup);
    1637             :     }
    1638             :     else
    1639             :     {
    1640          14 :         poRootGroup->poHchild = nullptr;
    1641             :     }
    1642             : 
    1643         139 :     HDF5ListGroupObjects(poRootGroup, bSUBDATASET);
    1644         139 :     return CE_None;
    1645             : }
    1646             : 
    1647             : /**
    1648             :  * Reads an array of double attributes from the HDF5 metadata.
    1649             :  * It reads the attributes directly on its binary form directly,
    1650             :  * thus avoiding string conversions.
    1651             :  *
    1652             :  * Important: It allocates the memory for the attributes internally,
    1653             :  * so the caller must free the returned array after using it.
    1654             :  * @param pszAttrFullPath Name of the attribute to be read.
    1655             :  *        the attribute name must be the form:
    1656             :  *            root attribute name
    1657             :  *            SUBDATASET/subdataset attribute name
    1658             :  * @param pdfValues pointer which will store the array of doubles read.
    1659             :  * @param nLen it stores the length of the array read. If NULL it doesn't
    1660             :  *        inform the length of the array.
    1661             :  * @return CPLErr CE_None in case of success, CE_Failure in case of failure
    1662             :  */
    1663          10 : CPLErr HDF5Dataset::HDF5ReadDoubleAttr(const char *pszAttrFullPath,
    1664             :                                        double **pdfValues, int *nLen)
    1665             : {
    1666          20 :     CPLString osAttrFullPath(pszAttrFullPath);
    1667             : 
    1668             :     // Search for the last "/" in order to get the path to the attribute.
    1669          10 :     const size_t nSlashPos = osAttrFullPath.find_last_of("/");
    1670             : 
    1671          20 :     CPLString osObjName;
    1672          10 :     CPLString osAttrName;
    1673             : 
    1674             :     // If objects name have been found.
    1675          10 :     if (nSlashPos != CPLString::npos)
    1676             :     {
    1677             :         // Split Object name (dataset, group).
    1678           7 :         osObjName = osAttrFullPath.substr(0, nSlashPos);
    1679             :         // Split attribute name.
    1680           7 :         osAttrName = osAttrFullPath.substr(nSlashPos + 1);
    1681             :     }
    1682             :     else
    1683             :     {
    1684             :         // By default the group is root, and
    1685             :         // the attribute is the full path.
    1686           3 :         osObjName = "/";
    1687           3 :         osAttrName = pszAttrFullPath;
    1688             :     }
    1689             : 
    1690          10 :     const hid_t hObjAttrID = H5Oopen(m_hHDF5, osObjName.c_str(), H5P_DEFAULT);
    1691             : 
    1692          10 :     CPLErr retVal = CE_Failure;
    1693             : 
    1694          10 :     if (hObjAttrID < 0)
    1695             :     {
    1696           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Object %s could not be opened",
    1697             :                  pszAttrFullPath);
    1698           0 :         retVal = CE_Failure;
    1699             :     }
    1700             :     else
    1701             :     {
    1702             :         // Open attribute handler by name, from the object handler opened
    1703             :         // earlier.
    1704          10 :         const hid_t hAttrID = H5Aopen_name(hObjAttrID, osAttrName.c_str());
    1705             : 
    1706             :         // Check for errors opening the attribute.
    1707          10 :         if (hAttrID < 0)
    1708             :         {
    1709           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1710             :                      "Attribute %s could not be opened", pszAttrFullPath);
    1711           0 :             retVal = CE_Failure;
    1712             :         }
    1713             :         else
    1714             :         {
    1715          10 :             const hid_t hAttrTypeID = H5Aget_type(hAttrID);
    1716             :             const hid_t hAttrNativeType =
    1717          10 :                 H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
    1718          10 :             const hid_t hAttrSpace = H5Aget_space(hAttrID);
    1719          10 :             hsize_t nSize[64] = {};
    1720             :             const unsigned int nAttrDims =
    1721          10 :                 H5Sget_simple_extent_dims(hAttrSpace, nSize, nullptr);
    1722             : 
    1723          10 :             if (!H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType))
    1724             :             {
    1725           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
    1726             :                          "Attribute %s is not of type double", pszAttrFullPath);
    1727           0 :                 retVal = CE_Failure;
    1728             :             }
    1729             :             else
    1730             :             {
    1731             :                 // Get the amount of elements.
    1732          10 :                 int nAttrElmts = 1;
    1733          17 :                 for (hsize_t i = 0; i < nAttrDims; i++)
    1734             :                 {
    1735          14 :                     if (nSize[i] >
    1736           7 :                         static_cast<hsize_t>(std::numeric_limits<int>::max() /
    1737             :                                              nAttrElmts))
    1738             :                     {
    1739           0 :                         nAttrElmts = 0;
    1740           0 :                         break;
    1741             :                     }
    1742             :                     // For multidimensional attributes
    1743           7 :                     nAttrElmts *= static_cast<int>(nSize[i]);
    1744             :                 }
    1745             : 
    1746          10 :                 if (nLen != nullptr)
    1747           0 :                     *nLen = nAttrElmts;
    1748             : 
    1749          10 :                 *pdfValues = static_cast<double *>(
    1750          10 :                     nAttrElmts ? VSI_MALLOC2_VERBOSE(nAttrElmts, sizeof(double))
    1751             :                                : nullptr);
    1752             : 
    1753             :                 // Read the attribute contents
    1754          20 :                 if (nAttrElmts == 0 ||
    1755          10 :                     H5Aread(hAttrID, hAttrNativeType, *pdfValues) < 0)
    1756             :                 {
    1757           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1758             :                              "Attribute %s could not be opened",
    1759             :                              pszAttrFullPath);
    1760           0 :                     retVal = CE_Failure;
    1761             :                 }
    1762             :                 else
    1763             :                 {
    1764          10 :                     retVal = CE_None;
    1765             :                 }
    1766             :             }
    1767             : 
    1768          10 :             H5Tclose(hAttrNativeType);
    1769          10 :             H5Tclose(hAttrTypeID);
    1770          10 :             H5Sclose(hAttrSpace);
    1771          10 :             H5Aclose(hAttrID);
    1772             :         }
    1773          10 :         H5Oclose(hObjAttrID);
    1774             :     }
    1775             : 
    1776          20 :     return retVal;
    1777             : }

Generated by: LCOV version 1.14