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 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included
21 * in all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 * DEALINGS IN THE SOFTWARE.
30 ****************************************************************************/
31
32 #include "cpl_port.h"
33
34 #include "hdf5_api.h"
35 #include "hdf5dataset.h"
36 #include "hdf5vfl.h"
37
38 #include <algorithm>
39 #include <stdio.h>
40 #include <string.h>
41 #include <string>
42
43 #include "cpl_conv.h"
44 #include "cpl_error.h"
45 #include "cpl_string.h"
46 #include "gdal.h"
47 #include "gdal_frmts.h"
48 #include "gdal_priv.h"
49
50 CPL_CVSID("$Id: hdf5dataset.cpp 95a15ba5f7ec811536faa84323d9898ea404a555 2020-11-23 17:07:35 +0100 Even Rouault $")
51
52 constexpr size_t MAX_METADATA_LEN = 32768;
53
54 /************************************************************************/
55 /* HDF5GetFileDriver() */
56 /************************************************************************/
57
HDF5GetFileDriver()58 hid_t HDF5GetFileDriver()
59 {
60 return HDF5VFLGetFileDriver();
61 }
62
63 /************************************************************************/
64 /* HDF5UnloadFileDriver() */
65 /************************************************************************/
66
HDF5UnloadFileDriver()67 void HDF5UnloadFileDriver()
68 {
69 HDF5VFLUnloadFileDriver();
70 }
71
72 /************************************************************************/
73 /* HDF5DatasetDriverUnload() */
74 /************************************************************************/
75
HDF5DatasetDriverUnload(GDALDriver *)76 static void HDF5DatasetDriverUnload(GDALDriver*)
77 {
78 HDF5UnloadFileDriver();
79 }
80
81 /************************************************************************/
82 /* ==================================================================== */
83 /* HDF5Dataset */
84 /* ==================================================================== */
85 /************************************************************************/
86
87 /************************************************************************/
88 /* GDALRegister_HDF5() */
89 /************************************************************************/
GDALRegister_HDF5()90 void GDALRegister_HDF5()
91
92 {
93 if( GDALGetDriverByName("HDF5") != nullptr )
94 return;
95
96 GDALDriver *poDriver = new GDALDriver();
97
98 poDriver->SetDescription("HDF5");
99 poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
100 poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
101 "Hierarchical Data Format Release 5");
102 poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/hdf5.html");
103 poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "h5 hdf5");
104 poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
105 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
106
107 poDriver->SetMetadataItem( GDAL_DCAP_MULTIDIM_RASTER, "YES" );
108
109 poDriver->pfnOpen = HDF5Dataset::Open;
110 poDriver->pfnIdentify = HDF5Dataset::Identify;
111 poDriver->pfnUnloadDriver = HDF5DatasetDriverUnload;
112 GetGDALDriverManager()->RegisterDriver(poDriver);
113
114 #ifdef HDF5_PLUGIN
115 GDALRegister_HDF5Image();
116 GDALRegister_BAG();
117 #endif
118 }
119
120 /************************************************************************/
121 /* HDF5Dataset() */
122 /************************************************************************/
HDF5Dataset()123 HDF5Dataset::HDF5Dataset() :
124 hHDF5(-1),
125 hGroupID(-1),
126 papszSubDatasets(nullptr),
127 bIsHDFEOS(FALSE),
128 nDatasetType(-1),
129 nSubDataCount(0),
130 poH5RootGroup(nullptr),
131 papszMetadata(nullptr),
132 poH5CurrentObject(nullptr)
133 {}
134
135 /************************************************************************/
136 /* ~HDF5Dataset() */
137 /************************************************************************/
~HDF5Dataset()138 HDF5Dataset::~HDF5Dataset()
139 {
140 CSLDestroy(papszMetadata);
141 if( hGroupID > 0 )
142 H5Gclose(hGroupID);
143 if( hHDF5 > 0 )
144 H5Fclose(hHDF5);
145
146 CSLDestroy(papszSubDatasets);
147 if( poH5RootGroup != nullptr )
148 {
149 DestroyH5Objects(poH5RootGroup);
150 CPLFree(poH5RootGroup->pszName);
151 CPLFree(poH5RootGroup->pszPath);
152 CPLFree(poH5RootGroup->pszUnderscorePath);
153 CPLFree(poH5RootGroup->poHchild);
154 CPLFree(poH5RootGroup);
155 }
156 }
157
158 /************************************************************************/
159 /* GetDataType() */
160 /* */
161 /* Transform HDF5 datatype to GDAL datatype */
162 /************************************************************************/
GetDataType(hid_t TypeID)163 GDALDataType HDF5Dataset::GetDataType(hid_t TypeID)
164 {
165 //Check for native types first
166 if (H5Tget_class(TypeID) != H5T_COMPOUND)
167 {
168
169 if( H5Tequal(H5T_NATIVE_CHAR, TypeID) )
170 return GDT_Byte;
171 else if( H5Tequal(H5T_NATIVE_SCHAR, TypeID) )
172 return GDT_Byte;
173 else if( H5Tequal(H5T_NATIVE_UCHAR, TypeID) )
174 return GDT_Byte;
175 else if( H5Tequal(H5T_NATIVE_SHORT, TypeID) )
176 return GDT_Int16;
177 else if( H5Tequal(H5T_NATIVE_USHORT, TypeID) )
178 return GDT_UInt16;
179 else if( H5Tequal(H5T_NATIVE_INT, TypeID) )
180 return GDT_Int32;
181 else if( H5Tequal(H5T_NATIVE_UINT, TypeID) )
182 return GDT_UInt32;
183 else if( H5Tequal(H5T_NATIVE_LONG, TypeID) )
184 {
185 #if SIZEOF_UNSIGNED_LONG == 4
186 return GDT_Int32;
187 #else
188 return GDT_Unknown;
189 #endif
190 }
191 else if( H5Tequal(H5T_NATIVE_ULONG, TypeID) )
192 {
193 #if SIZEOF_UNSIGNED_LONG == 4
194 return GDT_UInt32;
195 #else
196 return GDT_Unknown;
197 #endif
198 }
199 else if( H5Tequal(H5T_NATIVE_FLOAT, TypeID) )
200 return GDT_Float32;
201 else if( H5Tequal(H5T_NATIVE_DOUBLE, TypeID) )
202 return GDT_Float64;
203 else if( H5Tequal(H5T_NATIVE_LLONG, TypeID) )
204 return GDT_Unknown;
205 else if( H5Tequal(H5T_NATIVE_ULLONG, TypeID) )
206 return GDT_Unknown;
207 }
208 else //Parse compound type to determine if data is complex
209 {
210 //For complex the compound type must contain 2 elements
211 if ( H5Tget_nmembers(TypeID) != 2 )
212 return GDT_Unknown;
213
214 //For complex the native types of both elements should be the same
215 hid_t ElemTypeID = H5Tget_member_type(TypeID, 0);
216 hid_t Elem2TypeID = H5Tget_member_type(TypeID, 1);
217 const bool bTypeEqual = H5Tequal( ElemTypeID, Elem2TypeID) > 0;
218 H5Tclose(Elem2TypeID);
219 if ( !bTypeEqual )
220 {
221 H5Tclose(ElemTypeID);
222 return GDT_Unknown;
223 }
224
225 char* pszName1 = H5Tget_member_name(TypeID, 0);
226 const bool bIsReal = pszName1 && (pszName1[0] == 'r' ||pszName1[0] == 'R');
227 H5free_memory(pszName1);
228
229 char* pszName2 = H5Tget_member_name(TypeID, 1);
230 const bool bIsImaginary = pszName2 && (pszName2[0] == 'i' ||pszName2[0] == 'I');
231 H5free_memory(pszName2);
232
233 if( !bIsReal || !bIsImaginary)
234 {
235 H5Tclose(ElemTypeID);
236 return GDT_Unknown;
237 }
238
239 //Check the native types to determine CInt16, CFloat32 or CFloat64
240 GDALDataType eDataType = GDT_Unknown;
241
242 if ( H5Tequal(H5T_NATIVE_SHORT, ElemTypeID) )
243 eDataType = GDT_CInt16;
244 else if ( H5Tequal(H5T_NATIVE_INT, ElemTypeID) )
245 eDataType = GDT_CInt32;
246 else if ( H5Tequal(H5T_NATIVE_LONG, ElemTypeID) )
247 {
248 #if SIZEOF_UNSIGNED_LONG == 4
249 eDataType = GDT_CInt32;
250 #else
251 eDataType = GDT_Unknown;
252 #endif
253 }
254 else if ( H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID) )
255 eDataType = GDT_CFloat32;
256 else if ( H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID) )
257 eDataType = GDT_CFloat64;
258
259 //Close the data type
260 H5Tclose(ElemTypeID);
261
262 return eDataType;
263 }
264
265 return GDT_Unknown;
266 }
267
268 /************************************************************************/
269 /* GetDataTypeName() */
270 /* */
271 /* Return the human readable name of data type */
272 /************************************************************************/
GetDataTypeName(hid_t TypeID)273 const char *HDF5Dataset::GetDataTypeName(hid_t TypeID)
274 {
275 //Check for native types first
276 if (H5Tget_class(TypeID) != H5T_COMPOUND)
277 {
278 if( H5Tequal(H5T_NATIVE_CHAR, TypeID) )
279 return "8-bit character";
280 else if( H5Tequal(H5T_NATIVE_SCHAR, TypeID) )
281 return "8-bit signed character";
282 else if( H5Tequal(H5T_NATIVE_UCHAR, TypeID) )
283 return "8-bit unsigned character";
284 else if( H5Tequal(H5T_NATIVE_SHORT, TypeID) )
285 return "16-bit integer";
286 else if( H5Tequal(H5T_NATIVE_USHORT, TypeID) )
287 return "16-bit unsigned integer";
288 else if( H5Tequal(H5T_NATIVE_INT, TypeID) )
289 return "32-bit integer";
290 else if( H5Tequal(H5T_NATIVE_UINT, TypeID) )
291 return "32-bit unsigned integer";
292 else if( H5Tequal(H5T_NATIVE_LONG, TypeID) )
293 return "32/64-bit integer";
294 else if( H5Tequal(H5T_NATIVE_ULONG, TypeID) )
295 return "32/64-bit unsigned integer";
296 else if( H5Tequal(H5T_NATIVE_FLOAT, TypeID) )
297 return "32-bit floating-point";
298 else if( H5Tequal(H5T_NATIVE_DOUBLE, TypeID) )
299 return "64-bit floating-point";
300 else if( H5Tequal(H5T_NATIVE_LLONG, TypeID) )
301 return "64-bit integer";
302 else if( H5Tequal(H5T_NATIVE_ULLONG, TypeID) )
303 return "64-bit unsigned integer";
304 else if( H5Tequal(H5T_NATIVE_DOUBLE, TypeID) )
305 return "64-bit floating-point";
306 }
307 else
308 {
309 //For complex the compound type must contain 2 elements
310 if ( H5Tget_nmembers(TypeID) != 2 )
311 return "Unknown";
312
313 //For complex the native types of both elements should be the same
314 hid_t ElemTypeID = H5Tget_member_type(TypeID, 0);
315 hid_t Elem2TypeID = H5Tget_member_type(TypeID, 1);
316 const bool bTypeEqual = H5Tequal( ElemTypeID, Elem2TypeID) > 0;
317 H5Tclose(Elem2TypeID);
318 if ( !bTypeEqual )
319 {
320 H5Tclose(ElemTypeID);
321 return "Unknown";
322 }
323
324 //Check the native types to determine CInt16, CFloat32 or CFloat64
325 if ( H5Tequal(H5T_NATIVE_SHORT, ElemTypeID) )
326 {
327 H5Tclose(ElemTypeID);
328 return "complex, 16-bit integer";
329 }
330 else if ( H5Tequal(H5T_NATIVE_INT, ElemTypeID) )
331 {
332 H5Tclose(ElemTypeID);
333 return "complex, 32-bit integer";
334 }
335 else if ( H5Tequal(H5T_NATIVE_LONG, ElemTypeID) )
336 {
337 H5Tclose(ElemTypeID);
338 return "complex, 32/64-bit integer";
339 }
340 else if ( H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID) )
341 {
342 H5Tclose(ElemTypeID);
343 return "complex, 32-bit floating-point";
344 }
345 else if ( H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID) )
346 {
347 H5Tclose(ElemTypeID);
348 return "complex, 64-bit floating-point";
349 }
350 }
351
352
353 return "Unknown";
354 }
355
356 /************************************************************************/
357 /* Identify() */
358 /************************************************************************/
359
Identify(GDALOpenInfo * poOpenInfo)360 int HDF5Dataset::Identify( GDALOpenInfo * poOpenInfo )
361
362 {
363 if( (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) &&
364 STARTS_WITH(poOpenInfo->pszFilename, "HDF5:") )
365 {
366 return TRUE;
367 }
368
369 // Is it an HDF5 file?
370 constexpr char achSignature[] = "\211HDF\r\n\032\n";
371
372 if( !poOpenInfo->pabyHeader )
373 return FALSE;
374
375 const CPLString osExt(CPLGetExtension(poOpenInfo->pszFilename));
376
377 const auto IsRecognizedByNetCDFDriver = [&osExt, poOpenInfo]()
378 {
379 if( (EQUAL(osExt, "NC") ||
380 EQUAL(osExt, "CDF") ||
381 EQUAL(osExt, "NC4")) &&
382 GDALGetDriverByName("netCDF") != nullptr )
383 {
384 const char *const apszAllowedDriver[] = { "netCDF", nullptr };
385 CPLPushErrorHandler(CPLQuietErrorHandler);
386 GDALDatasetH hDS = GDALOpenEx(poOpenInfo->pszFilename,
387 GDAL_OF_RASTER | GDAL_OF_VECTOR,
388 apszAllowedDriver, nullptr, nullptr);
389 CPLPopErrorHandler();
390 if( hDS )
391 {
392 GDALClose(hDS);
393 return true;
394 }
395 }
396 return false;
397 };
398
399 if( memcmp(poOpenInfo->pabyHeader, achSignature, 8) == 0 )
400 {
401 // The tests to avoid opening KEA and BAG drivers are not
402 // necessary when drivers are built in the core lib, as they
403 // are registered after HDF5, but in the case of plugins, we
404 // cannot do assumptions about the registration order.
405
406 // Avoid opening kea files if the kea driver is available.
407 if( EQUAL(osExt, "KEA") && GDALGetDriverByName("KEA") != nullptr )
408 {
409 return FALSE;
410 }
411
412 // Avoid opening BAG files if the bag driver is available.
413 if( EQUAL(osExt, "BAG") && GDALGetDriverByName("BAG") != nullptr )
414 {
415 return FALSE;
416 }
417
418 // Avoid opening NC files if the netCDF driver is available and
419 // they are recognized by it.
420 if( IsRecognizedByNetCDFDriver() )
421 {
422 return FALSE;
423 }
424
425 return TRUE;
426 }
427
428 if( memcmp(poOpenInfo->pabyHeader, "<HDF_UserBlock>", 15) == 0)
429 {
430 if( H5Fis_hdf5(poOpenInfo->pszFilename) )
431 return TRUE;
432 }
433
434 // The HDF5 signature can be at offsets 512, 1024, 2048, etc.
435 if( poOpenInfo->fpL != nullptr &&
436 (EQUAL(osExt, "h5") || EQUAL(osExt, "hdf5") ||
437 EQUAL(osExt, "nc") || EQUAL(osExt, "cdf") || EQUAL(osExt, "nc4")) )
438 {
439 vsi_l_offset nOffset = 512;
440 for(int i = 0; i < 64; i++)
441 {
442 GByte abyBuf[8];
443 if( VSIFSeekL(poOpenInfo->fpL, nOffset, SEEK_SET) != 0 ||
444 VSIFReadL(abyBuf, 1, 8, poOpenInfo->fpL) != 8 )
445 {
446 break;
447 }
448 if( memcmp(abyBuf, achSignature, 8) == 0 )
449 {
450 // Avoid opening NC files if the netCDF driver is available and
451 // they are recognized by it.
452 if( IsRecognizedByNetCDFDriver() )
453 {
454 return FALSE;
455 }
456
457 return TRUE;
458 }
459 nOffset *= 2;
460 }
461 }
462
463 return FALSE;
464 }
465
466 /************************************************************************/
467 /* GDAL_HDF5Open() */
468 /************************************************************************/
GDAL_HDF5Open(const std::string & osFilename)469 hid_t GDAL_HDF5Open(const std::string& osFilename )
470 {
471 hid_t hHDF5;
472 // Heuristics to able datasets split over several files, using the 'family'
473 // driver. If passed the first file, and it contains a single 0, or
474 // ends up with 0.h5 or 0.hdf5, replace the 0 with %d and try the family driver.
475 if( std::count(osFilename.begin(), osFilename.end(), '0') == 1 ||
476 osFilename.find("0.h5") != std::string::npos ||
477 osFilename.find("0.hdf5") != std::string::npos )
478 {
479 const auto zero_pos = osFilename.rfind('0');
480 const auto osNewName = osFilename.substr(0, zero_pos) + "%d" + osFilename.substr(zero_pos+1);
481 hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
482 H5Pset_fapl_family(fapl, H5F_FAMILY_DEFAULT, H5P_DEFAULT);
483 #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
484 #pragma GCC diagnostic push
485 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
486 #endif
487 H5E_BEGIN_TRY {
488 hHDF5 = H5Fopen(osNewName.c_str(), H5F_ACC_RDONLY, fapl);
489 } H5E_END_TRY;
490 #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
491 #pragma GCC diagnostic pop
492 #endif
493 H5Pclose(fapl);
494 if( hHDF5 >= 0 )
495 {
496 CPLDebug("HDF5", "Actually opening %s with 'family' driver",
497 osNewName.c_str());
498 return hHDF5;
499 }
500 }
501
502 hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
503 H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
504 hHDF5 = H5Fopen(osFilename.c_str(), H5F_ACC_RDONLY, fapl);
505 H5Pclose(fapl);
506 return hHDF5;
507 }
508
509 /************************************************************************/
510 /* Open() */
511 /************************************************************************/
Open(GDALOpenInfo * poOpenInfo)512 GDALDataset *HDF5Dataset::Open( GDALOpenInfo *poOpenInfo )
513 {
514 if( !Identify(poOpenInfo) )
515 return nullptr;
516
517 if( poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER )
518 {
519 return OpenMultiDim(poOpenInfo);
520 }
521
522 // Create datasource.
523 HDF5Dataset *const poDS = new HDF5Dataset();
524
525 poDS->SetDescription(poOpenInfo->pszFilename);
526
527 // Try opening the dataset.
528 poDS->hHDF5 = GDAL_HDF5Open(poOpenInfo->pszFilename);
529 if( poDS->hHDF5 < 0 )
530 {
531 delete poDS;
532 return nullptr;
533 }
534
535 poDS->hGroupID = H5Gopen(poDS->hHDF5, "/");
536 if( poDS->hGroupID < 0 )
537 {
538 poDS->bIsHDFEOS = false;
539 delete poDS;
540 return nullptr;
541 }
542
543 poDS->bIsHDFEOS = true;
544 poDS->ReadGlobalAttributes(true);
545
546 poDS->SetMetadata(poDS->papszMetadata);
547
548 if( STARTS_WITH(CSLFetchNameValueDef(poDS->papszMetadata, "mission_name", ""), "Sentinel 3") &&
549 EQUAL(CSLFetchNameValueDef(poDS->papszMetadata, "altimeter_sensor_name", ""), "SRAL") &&
550 EQUAL(CSLFetchNameValueDef(poDS->papszMetadata, "radiometer_sensor_name", ""), "MWR") &&
551 GDALGetDriverByName("netCDF") != nullptr )
552 {
553 delete poDS;
554 return nullptr;
555 }
556
557 if ( CSLCount(poDS->papszSubDatasets) / 2 >= 1 )
558 poDS->SetMetadata(poDS->papszSubDatasets, "SUBDATASETS");
559
560 // Make sure we don't try to do any pam stuff with this dataset.
561 poDS->nPamFlags |= GPF_NOSAVE;
562
563 // If we have single subdataset only, open it immediately.
564 int nSubDatasets = CSLCount(poDS->papszSubDatasets) / 2;
565 if( nSubDatasets == 1 )
566 {
567 CPLString osDSName =
568 CSLFetchNameValue(poDS->papszSubDatasets, "SUBDATASET_1_NAME");
569 delete poDS;
570 return GDALDataset::Open(osDSName, poOpenInfo->nOpenFlags, nullptr,
571 poOpenInfo->papszOpenOptions, nullptr);
572 }
573 else
574 {
575 // Confirm the requested access is supported.
576 if( poOpenInfo->eAccess == GA_Update )
577 {
578 delete poDS;
579 CPLError(CE_Failure, CPLE_NotSupported,
580 "The HDF5 driver does not support update access to "
581 "existing datasets.");
582 return nullptr;
583 }
584 }
585 return poDS;
586 }
587
588 /************************************************************************/
589 /* DestroyH5Objects() */
590 /* */
591 /* Erase all objects */
592 /************************************************************************/
DestroyH5Objects(HDF5GroupObjects * poH5Object)593 void HDF5Dataset::DestroyH5Objects( HDF5GroupObjects *poH5Object )
594 {
595 // Visit all objects.
596 for( unsigned i = 0; i < poH5Object->nbObjs; i++ )
597 DestroyH5Objects(poH5Object->poHchild + i);
598
599 if( poH5Object->poHparent ==nullptr )
600 return;
601
602 // Erase some data.
603 CPLFree(poH5Object->paDims);
604 poH5Object->paDims = nullptr;
605
606 CPLFree(poH5Object->pszPath);
607 poH5Object->pszPath = nullptr;
608
609 CPLFree(poH5Object->pszName);
610 poH5Object->pszName = nullptr;
611
612 CPLFree(poH5Object->pszUnderscorePath);
613 poH5Object->pszUnderscorePath = nullptr;
614
615 if( poH5Object->native > 0 )
616 H5Tclose(poH5Object->native);
617 poH5Object->native = 0;
618
619 // All Children are visited and can be deleted.
620 if( poH5Object->nbObjs != 0 )
621 {
622 CPLFree(poH5Object->poHchild);
623 poH5Object->poHchild = nullptr;
624 }
625 }
626
627 /************************************************************************/
628 /* CreatePath() */
629 /* */
630 /* Find Dataset path for HDopen */
631 /************************************************************************/
CreatePath(HDF5GroupObjects * poH5Object)632 static void CreatePath( HDF5GroupObjects *poH5Object )
633 {
634 // Recurse to the root path.
635 CPLString osPath;
636 if( poH5Object->poHparent != nullptr )
637 {
638 CreatePath(poH5Object->poHparent);
639 osPath = poH5Object->poHparent->pszPath;
640 }
641
642 // Add name to the path.
643 if( !EQUAL(poH5Object->pszName, "/") )
644 {
645 osPath.append("/");
646 osPath.append(poH5Object->pszName);
647 }
648
649 // Fill up path for each object.
650 CPLString osUnderscoreSpaceInName;
651 if( poH5Object->pszPath == nullptr )
652 {
653 // This is completely useless but needed if we want to keep
654 // subdataset names as they have "always" been formatted,
655 // with double slash at the beginning
656 if( osPath.empty() )
657 osPath = "/";
658
659 // Change space for underscore.
660 char **papszPath =
661 CSLTokenizeString2(osPath.c_str(), " ", CSLT_HONOURSTRINGS);
662
663 for( int i = 0; papszPath[i] != nullptr ; i++ )
664 {
665 if( i > 0 )
666 osUnderscoreSpaceInName.append("_");
667 osUnderscoreSpaceInName.append(papszPath[i]);
668 }
669 CSLDestroy(papszPath);
670
671 // -1 to give room for NUL in C strings.
672 constexpr size_t MAX_PATH = 8192 - 1;
673 // TODO(schwehr): Is it an issue if the results are longer than 8192?
674 // It appears that the output can never be longer than the source.
675 if( osUnderscoreSpaceInName.size() > MAX_PATH )
676 CPLError(CE_Fatal, CPLE_AppDefined,
677 "osUnderscoreSpaceInName longer than MAX_PATH: "
678 "%u > %u",
679 static_cast<unsigned int>(osUnderscoreSpaceInName.size()),
680 static_cast<unsigned int>(MAX_PATH));
681 if( osPath.size() > MAX_PATH )
682 CPLError(CE_Fatal, CPLE_AppDefined,
683 "osPath longer than MAX_PATH: %u > %u",
684 static_cast<unsigned int>(osPath.size()),
685 static_cast<unsigned int>(MAX_PATH));
686
687 poH5Object->pszUnderscorePath =
688 CPLStrdup(osUnderscoreSpaceInName.c_str());
689 poH5Object->pszPath = CPLStrdup(osPath.c_str());
690 }
691 }
692
693 /************************************************************************/
694 /* HDF5GroupCheckDuplicate() */
695 /* */
696 /* Returns TRUE if an ancestor has the same objno[] as passed */
697 /* in - used to avoid looping in files with "links up" #(3218). */
698 /************************************************************************/
699
HDF5GroupCheckDuplicate(HDF5GroupObjects * poHparent,unsigned long * objno)700 static int HDF5GroupCheckDuplicate( HDF5GroupObjects *poHparent,
701 unsigned long *objno )
702
703 {
704 while( poHparent != nullptr )
705 {
706 if( poHparent->objno[0] == objno[0] &&
707 poHparent->objno[1] == objno[1] )
708 return TRUE;
709
710 poHparent = poHparent->poHparent;
711 }
712
713 return FALSE;
714 }
715
716 /************************************************************************/
717 /* HDF5CreateGroupObjs() */
718 /* */
719 /* Create HDF5 hierarchy into a linked list */
720 /************************************************************************/
HDF5CreateGroupObjs(hid_t hHDF5,const char * pszObjName,void * poHObjParent)721 herr_t HDF5CreateGroupObjs( hid_t hHDF5, const char *pszObjName,
722 void *poHObjParent)
723 {
724 HDF5GroupObjects *const poHparent =
725 static_cast<HDF5GroupObjects *>(poHObjParent);
726 HDF5GroupObjects *poHchild = poHparent->poHchild;
727 H5G_stat_t oStatbuf;
728
729 if( H5Gget_objinfo(hHDF5, pszObjName, FALSE, &oStatbuf) < 0 )
730 return -1;
731
732 // Look for next child.
733 unsigned idx = 0; // idx is used after the for loop.
734 for( ; idx < poHparent->nbObjs; idx++ )
735 {
736 if( poHchild->pszName == nullptr ) break;
737 poHchild++;
738 }
739
740 if( idx == poHparent->nbObjs )
741 return -1; // All children parsed.
742
743 // Save child information.
744 poHchild->pszName = CPLStrdup(pszObjName);
745
746 poHchild->nType = oStatbuf.type;
747 poHchild->nIndex = idx;
748 poHchild->poHparent = poHparent;
749 poHchild->nRank = 0;
750 poHchild->paDims = nullptr;
751 poHchild->HDatatype = 0;
752 poHchild->objno[0] = oStatbuf.objno[0];
753 poHchild->objno[1] = oStatbuf.objno[1];
754 if( poHchild->pszPath == nullptr )
755 {
756 CreatePath(poHchild);
757 }
758 if( poHparent->pszPath == nullptr )
759 {
760 CreatePath(poHparent);
761 }
762
763 switch( oStatbuf.type )
764 {
765 case H5G_LINK:
766 {
767 poHchild->nbAttrs = 0;
768 poHchild->nbObjs = 0;
769 poHchild->poHchild = nullptr;
770 poHchild->nRank = 0;
771 poHchild->paDims = nullptr;
772 poHchild->HDatatype = 0;
773 break;
774 }
775 case H5G_GROUP:
776 {
777 hid_t hGroupID = H5I_INVALID_HID; // Identifier of group.
778 if( (hGroupID = H5Gopen(hHDF5, pszObjName)) == -1)
779 {
780 CPLError(CE_Failure, CPLE_AppDefined,
781 "unable to access \"%s\" group.", pszObjName);
782 return -1;
783 }
784 // Number of attributes in object.
785 const int nbAttrs = H5Aget_num_attrs(hGroupID);
786 hsize_t nbObjs = 0; // Number of objects in a group.
787 H5Gget_num_objs(hGroupID, &nbObjs);
788 poHchild->nbAttrs = nbAttrs;
789 poHchild->nbObjs = static_cast<int>(nbObjs);
790 poHchild->nRank = 0;
791 poHchild->paDims = nullptr;
792 poHchild->HDatatype = 0;
793
794 if( nbObjs > 0 )
795 {
796 poHchild->poHchild = static_cast<HDF5GroupObjects *>(
797 CPLCalloc(static_cast<int>(nbObjs), sizeof(HDF5GroupObjects)));
798 memset(poHchild->poHchild, 0,
799 static_cast<size_t>(sizeof(HDF5GroupObjects) * nbObjs));
800 }
801 else
802 {
803 poHchild->poHchild = nullptr;
804 }
805
806 if( !HDF5GroupCheckDuplicate(poHparent, oStatbuf.objno) )
807 H5Giterate(hHDF5, pszObjName, nullptr, HDF5CreateGroupObjs, poHchild);
808 else
809 CPLDebug("HDF5", "avoiding link looping on node '%s'.", pszObjName);
810
811 H5Gclose(hGroupID);
812 break;
813 }
814 case H5G_DATASET:
815 {
816 hid_t hDatasetID = H5I_INVALID_HID; // Identifier of dataset.
817 if( (hDatasetID = H5Dopen(hHDF5, pszObjName)) == -1)
818 {
819 CPLError(CE_Failure, CPLE_AppDefined,
820 "unable to access \"%s\" dataset.", pszObjName);
821 return -1;
822 }
823 const int nbAttrs = H5Aget_num_attrs(hDatasetID);
824 const hid_t datatype = H5Dget_type(hDatasetID);
825 const hid_t dataspace = H5Dget_space(hDatasetID);
826 const int n_dims = H5Sget_simple_extent_ndims(dataspace);
827 const hid_t native = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
828 hsize_t *maxdims = nullptr;
829 hsize_t *dims = nullptr;
830
831 if( n_dims > 0 )
832 {
833 dims = static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t)));
834 maxdims =
835 static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t)));
836 }
837 H5Sget_simple_extent_dims(dataspace, dims, maxdims);
838 if( maxdims != nullptr )
839 CPLFree(maxdims);
840
841 if( n_dims > 0 )
842 {
843 poHchild->nRank = n_dims; // rank of the array
844 poHchild->paDims = dims; // dimension of the array.
845 poHchild->HDatatype = datatype; // HDF5 datatype
846 }
847 else
848 {
849 poHchild->nRank = -1;
850 poHchild->paDims = nullptr;
851 poHchild->HDatatype = 0;
852 }
853 poHchild->nbAttrs = nbAttrs;
854 poHchild->nbObjs = 0;
855 poHchild->poHchild = nullptr;
856 poHchild->native = native;
857 H5Tclose(datatype);
858 H5Sclose(dataspace);
859 H5Dclose(hDatasetID);
860 break;
861 }
862 case H5G_TYPE:
863 {
864 poHchild->nbAttrs = 0;
865 poHchild->nbObjs = 0;
866 poHchild->poHchild = nullptr;
867 poHchild->nRank = 0;
868 poHchild->paDims = nullptr;
869 poHchild->HDatatype = 0;
870 break;
871 }
872 default:
873 break;
874 }
875
876 return 0;
877 }
878
879 /************************************************************************/
880 /* HDF5AttrIterate() */
881 /************************************************************************/
882
HDF5AttrIterate(hid_t hH5ObjID,const char * pszAttrName,void * pDS)883 static herr_t HDF5AttrIterate( hid_t hH5ObjID,
884 const char *pszAttrName,
885 // TODO(schwehr): void * -> HDF5Dataset *
886 void *pDS )
887 {
888 char **papszTokens = nullptr;
889 CPLString osKey;
890 HDF5Dataset *const poDS = static_cast<HDF5Dataset *>(pDS);
891
892 // Convert "/" into "_" for the path component
893 const char *pszPath = poDS->poH5CurrentObject->pszUnderscorePath;
894 if(pszPath != nullptr && strlen(pszPath) > 0)
895 {
896 papszTokens = CSLTokenizeString2(pszPath, "/", CSLT_HONOURSTRINGS);
897
898 for( hsize_t i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; ++i )
899 {
900 if( i != 0)
901 osKey += '_';
902 osKey += papszTokens[i];
903 }
904 CSLDestroy(papszTokens);
905 }
906
907 // Convert whitespaces into "_" for the attribute name component
908 papszTokens = CSLTokenizeString2(
909 pszAttrName, " ", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
910 for( hsize_t i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; ++i )
911 {
912 if(!osKey.empty())
913 osKey += '_';
914 osKey += papszTokens[i];
915 }
916 CSLDestroy(papszTokens);
917
918 const hid_t hAttrID = H5Aopen_name(hH5ObjID, pszAttrName);
919 const hid_t hAttrTypeID = H5Aget_type(hAttrID);
920 const hid_t hAttrNativeType =
921 H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
922 const hid_t hAttrSpace = H5Aget_space(hAttrID);
923
924 if( H5Tget_class(hAttrNativeType) == H5T_VLEN )
925 {
926 H5Sclose(hAttrSpace);
927 H5Tclose(hAttrNativeType);
928 H5Tclose(hAttrTypeID);
929 H5Aclose(hAttrID);
930 return 0;
931 }
932
933 hsize_t nSize[64] = {};
934 const unsigned int nAttrDims =
935 H5Sget_simple_extent_dims(hAttrSpace, nSize, nullptr);
936
937 unsigned int nAttrElmts = 1;
938 for( hsize_t i = 0; i < nAttrDims; i++ )
939 {
940 nAttrElmts *= static_cast<int>(nSize[i]);
941 }
942
943 char *szData = nullptr;
944 char *szValue = nullptr;
945
946 if( H5Tget_class(hAttrNativeType) == H5T_STRING )
947 {
948 if ( H5Tis_variable_str(hAttrNativeType) )
949 {
950 char **papszStrings =
951 static_cast<char **>(CPLMalloc(nAttrElmts * sizeof(char *)));
952
953 // Read the values.
954 H5Aread(hAttrID, hAttrNativeType, papszStrings);
955
956 // Concatenate all values as one string separated by a space.
957 CPLString osVal = papszStrings[0] ? papszStrings[0] : "{NULL}";
958 for( hsize_t i = 1; i < nAttrElmts; i++ )
959 {
960 osVal += " ";
961 osVal += papszStrings[i] ? papszStrings[i] : "{NULL}";
962 }
963
964 szValue = static_cast<char *>(CPLMalloc(osVal.length() + 1));
965 strcpy(szValue, osVal.c_str());
966
967 H5Dvlen_reclaim(hAttrNativeType, hAttrSpace, H5P_DEFAULT,
968 papszStrings);
969 CPLFree(papszStrings);
970 }
971 else
972 {
973 const hsize_t nAttrSize = H5Aget_storage_size(hAttrID);
974 szValue = static_cast<char *>(CPLMalloc((size_t)(nAttrSize + 1)));
975 H5Aread(hAttrID, hAttrNativeType, szValue);
976 szValue[nAttrSize] = '\0';
977 }
978 }
979 else
980 {
981 const size_t nDataLen = 8192;
982 void *buf = nullptr;
983
984 if( nAttrElmts > 0 )
985 {
986 buf = CPLMalloc(nAttrElmts * H5Tget_size(hAttrNativeType));
987 szData = static_cast<char *>(CPLMalloc(nDataLen));
988 szValue = static_cast<char *>(CPLMalloc(MAX_METADATA_LEN));
989 szData[0] = '\0';
990 szValue[0] = '\0';
991 H5Aread(hAttrID, hAttrNativeType, buf);
992 }
993 const bool bIsSCHAR = H5Tequal(H5T_NATIVE_SCHAR, hAttrNativeType) > 0;
994 const bool bIsUCHAR = H5Tequal(H5T_NATIVE_UCHAR, hAttrNativeType) > 0;
995 if( (bIsSCHAR || bIsUCHAR) &&
996 CPLTestBool(CPLGetConfigOption("GDAL_HDF5_CHAR_AS_STRING", "NO")) )
997 {
998 // Compatibility mode with ancient GDAL versions where we consider
999 // array of SCHAR/UCHAR as strings. Likely inappropriate mode...
1000 for( hsize_t i = 0; i < nAttrElmts; i++ )
1001 {
1002 snprintf(szData, nDataLen, "%c",
1003 static_cast<char *>(buf)[i]);
1004 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1005 MAX_METADATA_LEN )
1006 CPLError(CE_Warning, CPLE_OutOfMemory,
1007 "Header data too long. Truncated");
1008 }
1009 }
1010 else if( bIsSCHAR )
1011 {
1012 for( hsize_t i = 0; i < nAttrElmts; i++ )
1013 {
1014 snprintf(szData, nDataLen, "%d ",
1015 static_cast<signed char *>(buf)[i]);
1016 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1017 MAX_METADATA_LEN )
1018 CPLError(CE_Warning, CPLE_OutOfMemory,
1019 "Header data too long. Truncated");
1020 }
1021 }
1022 else if( bIsUCHAR )
1023 {
1024 for( hsize_t i = 0; i < nAttrElmts; i++ )
1025 {
1026 snprintf(szData, nDataLen, "%u ",
1027 static_cast<unsigned char *>(buf)[i]);
1028 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1029 MAX_METADATA_LEN )
1030 CPLError(CE_Warning, CPLE_OutOfMemory,
1031 "Header data too long. Truncated");
1032 }
1033 }
1034 else if( H5Tequal(H5T_NATIVE_SHORT, hAttrNativeType) > 0 )
1035 {
1036 for( hsize_t i = 0; i < nAttrElmts; i++ )
1037 {
1038 snprintf(szData, nDataLen, "%d ", static_cast<short *>(buf)[i]);
1039 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1040 MAX_METADATA_LEN )
1041 CPLError(CE_Warning, CPLE_OutOfMemory,
1042 "Header data too long. Truncated");
1043 }
1044 }
1045 else if( H5Tequal(H5T_NATIVE_USHORT, hAttrNativeType) > 0 )
1046 {
1047 for( hsize_t i = 0; i < nAttrElmts; i++ )
1048 {
1049 snprintf(szData, nDataLen, "%u ",
1050 static_cast<unsigned short *>(buf)[i]);
1051 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1052 MAX_METADATA_LEN )
1053 CPLError(CE_Warning, CPLE_OutOfMemory,
1054 "Header data too long. Truncated");
1055 }
1056 }
1057 else if( H5Tequal(H5T_NATIVE_INT, hAttrNativeType) > 0 )
1058 {
1059 for( hsize_t i=0; i < nAttrElmts; i++ )
1060 {
1061 snprintf(szData, nDataLen, "%d ", static_cast<int *>(buf)[i]);
1062 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1063 MAX_METADATA_LEN )
1064 CPLError(CE_Warning, CPLE_OutOfMemory,
1065 "Header data too long. Truncated");
1066 }
1067 }
1068 else if( H5Tequal(H5T_NATIVE_UINT, hAttrNativeType) > 0 )
1069 {
1070 for( hsize_t i = 0; i < nAttrElmts; i++ )
1071 {
1072 snprintf(szData, nDataLen, "%u ",
1073 static_cast<unsigned int *>(buf)[i]);
1074 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1075 MAX_METADATA_LEN )
1076 CPLError(CE_Warning, CPLE_OutOfMemory,
1077 "Header data too long. Truncated");
1078 }
1079 }
1080 else if( H5Tequal(H5T_NATIVE_LONG, hAttrNativeType) > 0 )
1081 {
1082 for( hsize_t i = 0; i < nAttrElmts; i++ )
1083 {
1084 snprintf(szData, nDataLen, "%ld ", static_cast<long *>(buf)[i]);
1085 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1086 MAX_METADATA_LEN )
1087 CPLError(CE_Warning, CPLE_OutOfMemory,
1088 "Header data too long. Truncated");
1089 }
1090 }
1091 else if( H5Tequal(H5T_NATIVE_ULONG, hAttrNativeType) > 0 )
1092 {
1093 for( hsize_t i = 0; i < nAttrElmts; i++ ) {
1094 snprintf(szData, nDataLen, "%lu ",
1095 static_cast<unsigned long *>(buf)[i]);
1096 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1097 MAX_METADATA_LEN )
1098 CPLError(CE_Warning, CPLE_OutOfMemory,
1099 "Header data too long. Truncated");
1100 }
1101 }
1102 else if( H5Tequal(H5T_NATIVE_FLOAT, hAttrNativeType) > 0 )
1103 {
1104 for( hsize_t i = 0; i < nAttrElmts; i++ )
1105 {
1106 CPLsnprintf(szData, nDataLen, "%.8g ",
1107 static_cast<float *>(buf)[i]);
1108 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1109 MAX_METADATA_LEN )
1110 CPLError(CE_Warning, CPLE_OutOfMemory,
1111 "Header data too long. Truncated");
1112 }
1113 }
1114 else if( H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType) > 0 )
1115 {
1116 for( hsize_t i = 0; i < nAttrElmts; i++ )
1117 {
1118 CPLsnprintf(szData, nDataLen, "%.15g ",
1119 static_cast<double *>(buf)[i]);
1120 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
1121 MAX_METADATA_LEN )
1122 CPLError(CE_Warning, CPLE_OutOfMemory,
1123 "Header data too long. Truncated");
1124 }
1125 }
1126 CPLFree(buf);
1127 }
1128 H5Sclose(hAttrSpace);
1129 H5Tclose(hAttrNativeType);
1130 H5Tclose(hAttrTypeID);
1131 H5Aclose(hAttrID);
1132 poDS->papszMetadata = CSLSetNameValue(poDS->papszMetadata, osKey, szValue);
1133
1134 CPLFree(szData);
1135 CPLFree(szValue);
1136
1137 return 0;
1138 }
1139
1140 /************************************************************************/
1141 /* CreateMetadata() */
1142 /************************************************************************/
CreateMetadata(HDF5GroupObjects * poH5Object,int nType)1143 CPLErr HDF5Dataset::CreateMetadata( HDF5GroupObjects *poH5Object, int nType)
1144 {
1145
1146 if( !poH5Object->pszPath )
1147 return CE_None;
1148
1149 poH5CurrentObject = poH5Object;
1150
1151 if( EQUAL(poH5Object->pszPath, "") )
1152 return CE_None;
1153
1154 HDF5Dataset *const poDS = this;
1155 const int nbAttrs = poH5Object->nbAttrs;
1156
1157 switch( nType )
1158 {
1159 case H5G_GROUP:
1160 if( nbAttrs > 0 )
1161 {
1162 // Identifier of group.
1163 const hid_t l_hGroupID = H5Gopen(hHDF5, poH5Object->pszPath);
1164 H5Aiterate(l_hGroupID, nullptr, HDF5AttrIterate, poDS);
1165 H5Gclose(l_hGroupID);
1166 }
1167 break;
1168 case H5G_DATASET:
1169 if( nbAttrs > 0 )
1170 {
1171 const hid_t hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath);
1172 H5Aiterate(hDatasetID, nullptr, HDF5AttrIterate, poDS);
1173 H5Dclose(hDatasetID);
1174 }
1175 break;
1176
1177 default:
1178 break;
1179 }
1180
1181 return CE_None;
1182 }
1183
1184 /************************************************************************/
1185 /* HDF5FindDatasetObjectsbyPath() */
1186 /* Find object by name */
1187 /************************************************************************/
1188 HDF5GroupObjects *
HDF5FindDatasetObjectsbyPath(HDF5GroupObjects * poH5Objects,const char * pszDatasetPath)1189 HDF5Dataset::HDF5FindDatasetObjectsbyPath( HDF5GroupObjects *poH5Objects,
1190 const char *pszDatasetPath )
1191 {
1192 if( poH5Objects->nType == H5G_DATASET &&
1193 EQUAL(poH5Objects->pszUnderscorePath, pszDatasetPath) )
1194 {
1195
1196 #ifdef DEBUG_VERBOSE
1197 printf("found it! %p\n", poH5Objects); /*ok*/
1198 #endif
1199 return poH5Objects;
1200 }
1201
1202 HDF5Dataset *const poDS = this;
1203
1204 if( poH5Objects->nbObjs > 0 )
1205 {
1206 for( unsigned int i = 0; i <poH5Objects->nbObjs; i++ ) {
1207 HDF5GroupObjects *poObjectsFound =
1208 poDS->HDF5FindDatasetObjectsbyPath(poH5Objects->poHchild + i,
1209 pszDatasetPath);
1210 // Is this our dataset?
1211 if( poObjectsFound != nullptr )
1212 return poObjectsFound;
1213 }
1214 }
1215 // Dataset has not been found.
1216 return nullptr;
1217 }
1218
1219 /************************************************************************/
1220 /* HDF5FindDatasetObjects() */
1221 /* Find object by name */
1222 /************************************************************************/
1223 HDF5GroupObjects *
HDF5FindDatasetObjects(HDF5GroupObjects * poH5Objects,const char * pszDatasetName)1224 HDF5Dataset::HDF5FindDatasetObjects( HDF5GroupObjects *poH5Objects,
1225 const char *pszDatasetName )
1226 {
1227 if( poH5Objects->nType == H5G_DATASET &&
1228 EQUAL(poH5Objects->pszName, pszDatasetName) )
1229 {
1230
1231 #ifdef DEBUG_VERBOSE
1232 printf("found it! %p\n", poH5Objects); /*ok*/
1233 #endif
1234 return poH5Objects;
1235 }
1236
1237 HDF5Dataset *poDS = this;
1238
1239 if( poH5Objects->nbObjs > 0 )
1240 {
1241 for( unsigned int i = 0; i <poH5Objects->nbObjs; i++ )
1242 {
1243 HDF5GroupObjects *poObjectsFound = poDS->HDF5FindDatasetObjects(
1244 poH5Objects->poHchild + i, pszDatasetName);
1245 // Is this our dataset?
1246 if( poObjectsFound != nullptr )
1247 return poObjectsFound;
1248 }
1249 }
1250
1251 // Dataset has not been found.
1252 return nullptr;
1253 }
1254
1255 /************************************************************************/
1256 /* HDF5ListGroupObjects() */
1257 /* */
1258 /* List all objects in HDF5 */
1259 /************************************************************************/
HDF5ListGroupObjects(HDF5GroupObjects * poRootGroup,int bSUBDATASET)1260 CPLErr HDF5Dataset::HDF5ListGroupObjects( HDF5GroupObjects *poRootGroup,
1261 int bSUBDATASET )
1262 {
1263 HDF5Dataset *poDS = this;
1264
1265 if( poRootGroup->nbObjs > 0 )
1266 for( hsize_t i = 0; i < poRootGroup->nbObjs; i++ )
1267 {
1268 poDS->HDF5ListGroupObjects(poRootGroup->poHchild + i, bSUBDATASET);
1269 }
1270
1271 if( poRootGroup->nType == H5G_GROUP ) {
1272 CreateMetadata(poRootGroup, H5G_GROUP);
1273 }
1274
1275 // Create Sub dataset list.
1276
1277 if( poRootGroup->nType == H5G_DATASET && bSUBDATASET &&
1278 poDS->GetDataType(poRootGroup->native) == GDT_Unknown )
1279 {
1280 CPLDebug("HDF5", "Skipping unsupported %s of type %s",
1281 poRootGroup->pszUnderscorePath,
1282 poDS->GetDataTypeName(poRootGroup->native));
1283 }
1284 else if( poRootGroup->nType == H5G_DATASET && bSUBDATASET )
1285 {
1286 CreateMetadata(poRootGroup, H5G_DATASET);
1287
1288 char szTemp[8192]; // TODO(schwehr): Get this off of the stack.
1289 switch( poRootGroup->nRank )
1290 {
1291 case 2:
1292 snprintf(szTemp, sizeof(szTemp), "%dx%d",
1293 static_cast<int>(poRootGroup->paDims[0]),
1294 static_cast<int>(poRootGroup->paDims[1]));
1295 break;
1296 case 3:
1297 snprintf(szTemp, sizeof(szTemp), "%dx%dx%d",
1298 static_cast<int>(poRootGroup->paDims[0]),
1299 static_cast<int>(poRootGroup->paDims[1]),
1300 static_cast<int>(poRootGroup->paDims[2]));
1301 break;
1302 default:
1303 return CE_None;
1304 }
1305
1306 const std::string osDim = szTemp;
1307
1308 snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME",
1309 ++(poDS->nSubDataCount));
1310
1311 poDS->papszSubDatasets =
1312 CSLSetNameValue(poDS->papszSubDatasets, szTemp,
1313 CPLSPrintf("HDF5:\"%s\":%s",
1314 poDS->GetDescription(),
1315 poRootGroup->pszUnderscorePath));
1316
1317 snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC",
1318 poDS->nSubDataCount);
1319
1320 poDS->papszSubDatasets = CSLSetNameValue(
1321 poDS->papszSubDatasets, szTemp,
1322 CPLSPrintf("[%s] %s (%s)", osDim.c_str(),
1323 poRootGroup->pszUnderscorePath,
1324 poDS->GetDataTypeName(poRootGroup->native)));
1325 }
1326
1327 return CE_None;
1328 }
1329
1330 /************************************************************************/
1331 /* ReadGlobalAttributes() */
1332 /************************************************************************/
ReadGlobalAttributes(int bSUBDATASET)1333 CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET)
1334 {
1335 HDF5GroupObjects *poRootGroup =
1336 static_cast<HDF5GroupObjects *>(CPLCalloc(sizeof(HDF5GroupObjects), 1));
1337
1338 poH5RootGroup = poRootGroup;
1339 poRootGroup->pszName = CPLStrdup("/");
1340 poRootGroup->nType = H5G_GROUP;
1341 poRootGroup->poHparent = nullptr;
1342 poRootGroup->pszPath = nullptr;
1343 poRootGroup->pszUnderscorePath = nullptr;
1344
1345 if( hHDF5 < 0 )
1346 {
1347 CPLError(CE_Failure, CPLE_AppDefined, "hHDF5 < 0!");
1348 return CE_None;
1349 }
1350
1351 H5G_stat_t oStatbuf = {{0, 0}, {0, 0}, 0, H5G_UNKNOWN, 0, 0, {0, 0, 0, 0}};
1352
1353 if( H5Gget_objinfo(hHDF5, "/", FALSE, &oStatbuf) < 0 )
1354 return CE_Failure;
1355 poRootGroup->objno[0] = oStatbuf.objno[0];
1356 poRootGroup->objno[1] = oStatbuf.objno[1];
1357
1358 if( hGroupID > 0 )
1359 H5Gclose(hGroupID);
1360 hGroupID = H5Gopen(hHDF5, "/");
1361 if( hGroupID < 0 )
1362 {
1363 CPLError(CE_Failure, CPLE_AppDefined, "hGroupId <0!");
1364 return CE_None;
1365 }
1366
1367 poRootGroup->nbAttrs = H5Aget_num_attrs(hGroupID);
1368
1369 H5Gget_num_objs(hGroupID, &(poRootGroup->nbObjs));
1370
1371 if( poRootGroup->nbObjs > 0 )
1372 {
1373 poRootGroup->poHchild = static_cast<HDF5GroupObjects *>(
1374 CPLCalloc(static_cast<size_t>(poRootGroup->nbObjs),
1375 sizeof(HDF5GroupObjects)));
1376 H5Giterate(hGroupID, "/", nullptr, HDF5CreateGroupObjs, poRootGroup);
1377 }
1378 else
1379 {
1380 poRootGroup->poHchild = nullptr;
1381 }
1382
1383 HDF5ListGroupObjects(poRootGroup, bSUBDATASET);
1384 return CE_None;
1385 }
1386
1387 /**
1388 * Reads an array of double attributes from the HDF5 metadata.
1389 * It reads the attributes directly on its binary form directly,
1390 * thus avoiding string conversions.
1391 *
1392 * Important: It allocates the memory for the attributes internally,
1393 * so the caller must free the returned array after using it.
1394 * @param pszAttrFullPath Name of the attribute to be read.
1395 * the attribute name must be the form:
1396 * root attribute name
1397 * SUBDATASET/subdataset attribute name
1398 * @param pdfValues pointer which will store the array of doubles read.
1399 * @param nLen it stores the length of the array read. If NULL it doesn't
1400 * inform the length of the array.
1401 * @return CPLErr CE_None in case of success, CE_Failure in case of failure
1402 */
HDF5ReadDoubleAttr(const char * pszAttrFullPath,double ** pdfValues,int * nLen)1403 CPLErr HDF5Dataset::HDF5ReadDoubleAttr(const char *pszAttrFullPath,
1404 double **pdfValues, int *nLen)
1405 {
1406 CPLString osAttrFullPath(pszAttrFullPath);
1407
1408 // Search for the last "/" in order to get the path to the attribute.
1409 const size_t nSlashPos = osAttrFullPath.find_last_of("/");
1410
1411 CPLString osObjName;
1412 CPLString osAttrName;
1413
1414 // If objects name have been found.
1415 if( nSlashPos != CPLString::npos )
1416 {
1417 // Split Object name (dataset, group).
1418 osObjName = osAttrFullPath.substr(0, nSlashPos);
1419 // Split attribute name.
1420 osAttrName = osAttrFullPath.substr(nSlashPos + 1);
1421 }
1422 else
1423 {
1424 // By default the group is root, and
1425 // the attribute is the full path.
1426 osObjName = "/";
1427 osAttrName = pszAttrFullPath;
1428 }
1429
1430 const hid_t hObjAttrID = H5Oopen(hHDF5, osObjName.c_str(), H5P_DEFAULT);
1431
1432 CPLErr retVal = CE_Failure;
1433
1434 if(hObjAttrID < 0)
1435 {
1436 CPLError(CE_Failure, CPLE_OpenFailed,
1437 "Object %s could not be opened", pszAttrFullPath);
1438 retVal = CE_Failure;
1439 }
1440 else
1441 {
1442 // Open attribute handler by name, from the object handler opened
1443 // earlier.
1444 const hid_t hAttrID = H5Aopen_name(hObjAttrID, osAttrName.c_str());
1445
1446 // Check for errors opening the attribute.
1447 if( hAttrID < 0 )
1448 {
1449 CPLError(CE_Failure, CPLE_OpenFailed,
1450 "Attribute %s could not be opened", pszAttrFullPath);
1451 retVal = CE_Failure;
1452 }
1453 else
1454 {
1455 const hid_t hAttrTypeID = H5Aget_type(hAttrID);
1456 const hid_t hAttrNativeType =
1457 H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
1458 const hid_t hAttrSpace = H5Aget_space(hAttrID);
1459 hsize_t nSize[64] = {};
1460 const unsigned int nAttrDims =
1461 H5Sget_simple_extent_dims(hAttrSpace, nSize, nullptr);
1462
1463 if( !H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType) )
1464 {
1465 CPLError(CE_Failure, CPLE_OpenFailed,
1466 "Attribute %s is not of type double",
1467 pszAttrFullPath);
1468 retVal = CE_Failure;
1469 }
1470 else
1471 {
1472 // Get the amount of elements.
1473 unsigned int nAttrElmts = 1;
1474 for( hsize_t i = 0; i < nAttrDims; i++ )
1475 {
1476 // For multidimensional attributes
1477 nAttrElmts *= static_cast<unsigned int>(nSize[i]);
1478 }
1479
1480 if(nLen != nullptr)
1481 *nLen = nAttrElmts;
1482
1483 *pdfValues = static_cast<double *>(
1484 CPLMalloc(nAttrElmts * sizeof(double)));
1485
1486 // Read the attribute contents
1487 if( H5Aread(hAttrID, hAttrNativeType, *pdfValues) < 0 )
1488 {
1489 CPLError(CE_Failure, CPLE_OpenFailed,
1490 "Attribute %s could not be opened",
1491 pszAttrFullPath);
1492 retVal = CE_Failure;
1493 }
1494 else
1495 {
1496 retVal = CE_None;
1497 }
1498 }
1499
1500 H5Tclose(hAttrNativeType);
1501 H5Tclose(hAttrTypeID);
1502 H5Sclose(hAttrSpace);
1503 H5Aclose(hAttrID);
1504 }
1505 H5Oclose(hObjAttrID);
1506 }
1507
1508 return retVal;
1509 }
1510