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