LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 654 833 78.5 %
Date: 2025-11-06 15:09:24 Functions: 23 24 95.8 %

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

Generated by: LCOV version 1.14