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