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