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