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