LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 643 808 79.6 %
Date: 2024-05-03 15:49:35 Functions: 23 23 100.0 %

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

Generated by: LCOV version 1.14