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