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

Generated by: LCOV version 1.14