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

Generated by: LCOV version 1.14