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