IODA
HH.cpp
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2020-2021 UCAR
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  */
7 /*! \addtogroup ioda_cxx_engines_pub_HH
8  *
9  * @{
10  * \file HH.cpp
11  * \brief HDF5 engine interface to the rest of ioda.
12  */
13 
14 #include "ioda/Engines/HH.h"
15 
16 #include <mutex>
17 #include <random>
18 #include <sstream>
19 
20 #include "./HH/HH-attributes.h"
21 #include "./HH/HH-groups.h"
22 #include "./HH/Handles.h"
23 #include "ioda/Exception.h"
24 #include "ioda/Group.h"
25 #include "ioda/defs.h"
26 
27 namespace ioda {
28 namespace Engines {
29 namespace HH {
30 unsigned int random_char() {
31  // Adapted from https://lowrey.me/guid-generation-in-c-11/
32  static std::random_device rd;
33  static std::mt19937 gen(rd());
34  static std::uniform_int_distribution<> dis(0, 255);
35  return dis(gen);
36 }
37 
38 std::string generate_hex(const unsigned int len) {
39  std::stringstream ss;
40  for (unsigned i = 0; i < len; i++) {
41  const auto rc = random_char();
42  std::stringstream hexstream;
43  hexstream << std::hex << rc;
44  auto hex = hexstream.str();
45  ss << (hex.length() < 2 ? '0' + hex : hex);
46  }
47  return ss.str();
48 }
49 
50 std::string genUniqueName() {
51  // GUIDs look like: {CD1A91C6-9C1B-454E-AD1C-977F4C72A01C}.
52  // We use these for the file name because they are quite unique.
53  // HDF5 needs unique names, otherwise it might open the same memory file twice.
54 
55  static std::mutex m;
56 
57  std::lock_guard<std::mutex> l{m};
58 
59  std::array<std::string, 5> uuid
61  std::string res
62  = uuid[0] + "-" + uuid[1] + "-" + uuid[2] + "-" + uuid[3] + "-" + uuid[4] + ".hdf5";
63  return res;
64 }
65 
66 const std::map<HDF5_Version, H5F_libver_t> map_h5ver
67  = {{HDF5_Version::Earliest, H5F_LIBVER_EARLIEST},
68  {HDF5_Version::V18, H5F_LIBVER_V18},
69 #if H5_VERSION_GE(1, 10, 0)
70  {HDF5_Version::V110, H5F_LIBVER_V110},
71 #endif
72 #if H5_VERSION_GE(1, 12, 0)
73  {HDF5_Version::V112, H5F_LIBVER_V112},
74 #endif
75  {HDF5_Version::Latest, H5F_LIBVER_LATEST}};
76 
78 #if H5_VERSION_GE(1, 10, 0)
80 #else
81  // Old HDF5 version fallthrough
83 #endif
84 }
85 
86 Group createMemoryFile(const std::string& filename, BackendCreateModes mode, bool flush_on_close,
87  size_t increment_len, HDF5_Version_Range compat) {
88  using namespace ioda::detail::Engines::HH;
89 
90  static const std::map<BackendCreateModes, unsigned int> m{
92  {BackendCreateModes::Fail_If_Exists, H5F_ACC_CREAT}};
93 
94  hid_t plid = H5Pcreate(H5P_FILE_ACCESS);
95  Expects(plid >= 0);
96  HH_hid_t pl(plid, Handles::Closers::CloseHDF5PropertyList::CloseP);
97 
98  if (0 > H5Pset_fapl_core(pl.get(), increment_len, flush_on_close))
99  throw Exception("H5Pset_fapl_core failed", ioda_Here());
100  // H5F_LIBVER_V18, H5F_LIBVER_V110, H5F_LIBVER_V112, H5F_LIBVER_LATEST.
101  // Note: this propagates to any files flushed to disk.
102  if (0 > H5Pset_libver_bounds(pl.get(), map_h5ver.at(compat.first), map_h5ver.at(compat.second)))
103  throw Exception("H5Pset_libver_bounds failed", ioda_Here());
104 
105  HH_hid_t f = H5Fcreate(filename.c_str(), m.at(mode), H5P_DEFAULT, pl.get());
106  if (f() < 0) throw Exception("H5Fcreate failed", ioda_Here());
107 
108  auto backend
109  = std::make_shared<detail::Engines::HH::HH_Group>(f, getCapabilitiesInMemoryEngine(), f);
110  return ::ioda::Group{backend};
111 }
112 
113 Group createFile(const std::string& filename, BackendCreateModes mode, HDF5_Version_Range compat) {
114  using namespace ioda::detail::Engines::HH;
115 
116  static const std::map<BackendCreateModes, unsigned int> m{
118  {BackendCreateModes::Fail_If_Exists, H5F_ACC_CREAT}};
119 
120  hid_t plid = H5Pcreate(H5P_FILE_ACCESS);
121  if (plid < 0) throw Exception("H5Pcreate failed", ioda_Here());
122  HH_hid_t pl(plid, Handles::Closers::CloseHDF5PropertyList::CloseP);
123  // H5F_LIBVER_V18, H5F_LIBVER_V110, H5F_LIBVER_V112, H5F_LIBVER_LATEST.
124  // Note: this propagates to any files flushed to disk.
125  if (0 > H5Pset_libver_bounds(pl.get(), map_h5ver.at(compat.first), map_h5ver.at(compat.second)))
126  throw Exception("H5Pset_libver_bounds failed", ioda_Here());
127 
128  HH_hid_t f = H5Fcreate(filename.c_str(), m.at(mode), H5P_DEFAULT, pl.get());
129  if (f() < 0) throw Exception("H5Fcreate failed", ioda_Here());
130 
131  auto backend = std::make_shared<detail::Engines::HH::HH_Group>(f, getCapabilitiesFileEngine(), f);
132  return ::ioda::Group{backend};
133 }
134 
135 Group openFile(const std::string& filename, BackendOpenModes mode, HDF5_Version_Range compat) {
136  using namespace ioda::detail::Engines::HH;
137  static const std::map<BackendOpenModes, unsigned int> m{
138  {BackendOpenModes::Read_Only, H5F_ACC_RDONLY}, {BackendOpenModes::Read_Write, H5F_ACC_RDWR}};
139 
140  hid_t plid = H5Pcreate(H5P_FILE_ACCESS);
141  if (plid < 0) throw Exception("H5Pcreate failed", ioda_Here());
142  HH_hid_t pl(plid, Handles::Closers::CloseHDF5PropertyList::CloseP);
143  if (0 > H5Pset_libver_bounds(pl.get(), map_h5ver.at(compat.first), map_h5ver.at(compat.second)))
144  throw Exception("H5Pset_libver_bounds failed", ioda_Here());
145 
146  HH_hid_t f = H5Fopen(filename.c_str(), m.at(mode), pl.get());
147  if (f() < 0) throw Exception("H5Fopen failed", ioda_Here());
148 
149  auto backend = std::make_shared<detail::Engines::HH::HH_Group>(f, getCapabilitiesFileEngine(), f);
150 
151  return ::ioda::Group{backend};
152 }
153 
154 Group openMemoryFile(const std::string& filename, BackendOpenModes mode, bool flush_on_close,
155  size_t increment_len, HDF5_Version_Range compat) {
156  using namespace ioda::detail::Engines::HH;
157  static const std::map<BackendOpenModes, unsigned int> m{
158  {BackendOpenModes::Read_Only, H5F_ACC_RDONLY}, {BackendOpenModes::Read_Write, H5F_ACC_RDWR}};
159 
160  hid_t plid = H5Pcreate(H5P_FILE_ACCESS);
161  Expects(plid >= 0);
162  HH_hid_t pl(plid, Handles::Closers::CloseHDF5PropertyList::CloseP);
163 
164  const auto h5Result = H5Pset_fapl_core(pl.get(), increment_len, flush_on_close);
165  if (h5Result < 0) throw Exception("H5Pset_fapl_core failed", ioda_Here());
166  if (0 > H5Pset_libver_bounds(pl.get(), map_h5ver.at(compat.first), map_h5ver.at(compat.second)))
167  throw Exception("H5Pset_libver_bounds failed", ioda_Here());
168 
169  HH_hid_t f = H5Fopen(filename.c_str(), m.at(mode), pl.get());
170  if (f() < 0) throw Exception("H5Fopen failed", ioda_Here());
171 
172  auto backend
173  = std::make_shared<detail::Engines::HH::HH_Group>(f, getCapabilitiesInMemoryEngine(), f);
174 
175  return ::ioda::Group{backend};
176 }
177 
179  static Capabilities caps;
180  static bool inited = false;
181  if (!inited) {
185 
186  bool canSZIP = false; //::HH::CanUseSZIP<int>();
189  }
190 
191  return caps;
192 }
193 
195  static Capabilities caps;
196  static bool inited = false;
197  if (!inited) {
201 
202  bool canSZIP = false; //::HH::CanUseSZIP<int>();
205  }
206 
207  return caps;
208 }
209 
210 } // namespace HH
211 } // namespace Engines
212 } // namespace ioda
213 
214 /// @}
IODA's error system.
Interfaces for ioda::Group and related classes.
HDF5 engine implementation of Attribute.
HDF5 group interface.
HDF5 engine.
HDF5 resource handles in C++.
The ioda exception class.
Definition: Exception.h:54
Groups are a new implementation of ObsSpaces.
Definition: Group.h:159
Common preprocessor definitions used throughout IODA.
IODA_DL Group createFile(const std::string &filename, BackendCreateModes mode, HDF5_Version_Range compat=defaultVersionRange())
Create a ioda::Group backed by an HDF5 file.
Definition: HH.cpp:113
IODA_DL HDF5_Version_Range defaultVersionRange()
Definition: HH.cpp:77
IODA_DL Group openFile(const std::string &filename, BackendOpenModes mode, HDF5_Version_Range compat=defaultVersionRange())
Open a ioda::Group backed by an HDF5 file.
Definition: HH.cpp:135
IODA_DL Capabilities getCapabilitiesFileEngine()
Get capabilities of the HDF5 file-backed engine.
Definition: HH.cpp:178
IODA_DL std::string genUniqueName()
Convenience function to generate a random file name.
Definition: HH.cpp:50
IODA_DL Capabilities getCapabilitiesInMemoryEngine()
Get capabilities of the HDF5 memory-backed engine.
Definition: HH.cpp:194
IODA_DL Group openMemoryFile(const std::string &filename, BackendOpenModes mode=BackendOpenModes::Read_Only, bool flush_on_close=false, size_t increment_len_bytes=1000000, HDF5_Version_Range compat=defaultVersionRange())
Map an HDF5 file in memory and open a ioda::Group.
Definition: HH.cpp:154
IODA_DL Group createMemoryFile(const std::string &filename, BackendCreateModes mode, bool flush_on_close=false, size_t increment_len_bytes=1000000, HDF5_Version_Range compat=defaultVersionRange())
Create a ioda::Group backed by the HDF5 in-memory-store.
Definition: HH.cpp:86
std::pair< HDF5_Version, HDF5_Version > HDF5_Version_Range
Definition: HH.h:40
@ Earliest
Use the earliest possible HDF5 format for storing objects.
@ V18
Use the latest HDF5 v1.8 format for storing objects.
@ V110
Use the latest HDF5 v1.10 format for storing objects.
@ Latest
Use the latest possible HDF5 format for storing objects.
@ V112
Use the latest HDF5 v1.12 format for storing objects.
BackendCreateModes
Definition: Factory.h:42
@ Unsupported
The feature causes an exception if used.
@ Supported
The feature always works.
@ Fail_If_Exists
If the file already exists, fail with an error.
@ Truncate_If_Exists
If the file already exists, overwrite it.
@ Read_Write
Open the file in read-write mode.
@ Read_Only
Open the file in read-only mode.
unsigned int random_char()
Definition: HH.cpp:30
std::string generate_hex(const unsigned int len)
Definition: HH.cpp:38
const std::map< HDF5_Version, H5F_libver_t > map_h5ver
Definition: HH.cpp:67
#define ioda_Here()
Struct defining what an engine can/cannot do.
Definition: Capabilities.h:47
Capability_Mask canCompressWithGZIP
Definition: Capabilities.h:49
Capability_Mask canChunk
Definition: Capabilities.h:48
Capability_Mask MPIaware
Definition: Capabilities.h:51
Capability_Mask canCompressWithSZIP
Definition: Capabilities.h:50