TRIQS/h5 2.0.0
C++ interface to HDF5
Loading...
Searching...
No Matches
string.cpp
Go to the documentation of this file.
1// Copyright (c) 2019-2024 Simons Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0.txt
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Authors: Thomas Hahn, Olivier Parcollet, Nils Wentzell
16
21
22#include "./string.hpp"
23#include "../macros.hpp"
24#include "../utils.hpp"
25
26#include <hdf5.h>
27#include <hdf5_hl.h>
28
29#include <algorithm>
30#include <array>
31#include <cstddef>
32#include <functional>
33#include <numeric>
34#include <stdexcept>
35#include <string>
36#include <utility>
37#include <vector>
38
39namespace h5 {
40
41 namespace {
42
43 // Returns an HDF5 datatype for a fixed-sized string with the given size or a variable-sized
44 // string if size == H5T_VARIABLE.
45 datatype str_dtype(size_t size = H5T_VARIABLE) {
46 datatype dt = H5Tcopy(H5T_C_S1);
47 auto err = H5Tset_size(dt, size);
48 H5Tset_cset(dt, H5T_CSET_UTF8);
49 if (err < 0) throw std::runtime_error("Error in str_dtype: H5Tset_size call failed");
50 return dt;
51 }
52
53 } // namespace
54
55 void h5_write(group g, std::string const &name, std::string const &s) {
56 // create the dataset for a variable-sized string
57 datatype dt = str_dtype();
58 dataspace space = H5Screate(H5S_SCALAR);
59 dataset ds = g.create_dataset(name, dt, space);
60
61 // write the string to dataset
62 auto *s_ptr = s.c_str();
63 auto err = H5Dwrite(ds, dt, H5S_ALL, H5S_ALL, H5P_DEFAULT, (void const *)&s_ptr);
64 if (err < 0) throw std::runtime_error("Error in h5_write: Writing a string to the dataset " + name + " in the group " + g.name() + " failed");
65 }
66
67 void h5_read(group g, std::string const &name, std::string &s) {
68 // clear the string
69 s = "";
70
71 // open the dataset and get dataspace and datatype information
72 dataset ds = g.open_dataset(name);
73 dataspace dspace = H5Dget_space(ds);
74 int rank = H5Sget_simple_extent_ndims(dspace);
75 if (rank != 0) throw std::runtime_error("Error in h5_read: Reading a string from a dataspace with rank != 0 is not possible");
76
77 datatype dt = H5Dget_type(ds);
78 H5_ASSERT(H5Tget_class(dt) == H5T_STRING);
79
80 // variable-sized string
81 if (H5Tis_variable_str(dt)) {
82 // first read into a char* pointer, then copy into the string
83 std::array<char *, 1> rd_ptr{nullptr};
84 auto err = H5Dread(ds, dt, H5S_ALL, H5S_ALL, H5P_DEFAULT, (void *)rd_ptr.data());
85 if (err < 0) throw std::runtime_error("Error in h5_read: Reading a string from the dataset " + name + " in the group " + g.name() + " failed");
86 s.append(rd_ptr[0]);
87
88 // free the resources allocated in the variable-length read
89 err = H5Dvlen_reclaim(dt, dspace, H5P_DEFAULT, (void *)rd_ptr.data());
90 if (err < 0) throw std::runtime_error("Error in h5_read: Freeing resources after reading a variable-length string failed");
91 } else { // fixed-sized string
92 std::vector<char> buf(H5Tget_size(dt) + 1, 0x00);
93 auto err = H5Dread(ds, dt, H5S_ALL, H5S_ALL, H5P_DEFAULT, &buf[0]);
94 if (err < 0) throw std::runtime_error("Error in h5_read: Reading a string from the dataset " + name + " in the group " + g.name() + " failed");
95 s.append(&buf.front());
96 }
97 }
98
99 void h5_write_attribute(object obj, std::string const &name, std::string const &s) {
100 // create the variable-sized string datatype and the dataspace
101 datatype dt = str_dtype();
102 dataspace space = H5Screate(H5S_SCALAR);
103
104 // create the attribute
105 attribute attr = H5Acreate2(obj, name.c_str(), dt, space, H5P_DEFAULT, H5P_DEFAULT);
106 if (!attr.is_valid()) throw std::runtime_error("Error in h5_write_attribute: Creating the attribute " + name + " failed");
107
108 // write the string to attribute
109 auto *s_ptr = s.c_str();
110 herr_t err = H5Awrite(attr, dt, (void const *)&s_ptr);
111 if (err < 0) throw std::runtime_error("Error in h5_write_attribute: Writing a string to the attribute " + name + " failed");
112 }
113
114 void h5_read_attribute(object obj, std::string const &name, std::string &s) {
115 // clear the string and return if the attribute is not present
116 s = "";
117 if (H5LTfind_attribute(obj, name.c_str()) == 0) return;
118
119 // open the attribute and get dataspace and datatype information
120 attribute attr = H5Aopen(obj, name.c_str(), H5P_DEFAULT);
121 dataspace dspace = H5Aget_space(attr);
122 int rank = H5Sget_simple_extent_ndims(dspace);
123 if (rank != 0) throw std::runtime_error("Error in h5_read_attribute: Reading a string from a dataspace with rank != 0 is not possible");
124
125 datatype dt = H5Aget_type(attr);
126 H5_ASSERT(H5Tget_class(dt) == H5T_STRING);
127
128 // variable-sized string
129 if (H5Tis_variable_str(dt)) {
130 // first read into a char* pointer, then copy into the string
131 std::array<char *, 1> rd_ptr{nullptr};
132 auto err = H5Aread(attr, dt, (void *)rd_ptr.data());
133 if (err < 0) throw std::runtime_error("Error in h5_read_attribute: Reading a string from the attribute " + name + " failed");
134 s.append(rd_ptr[0]);
135
136 // free the resources allocated in the variable-length read
137 err = H5Dvlen_reclaim(dt, dspace, H5P_DEFAULT, (void *)rd_ptr.data());
138 if (err < 0) throw std::runtime_error("Error in h5_read_attribute: Freeing resources after reading a variable-length string failed");
139 } else { // fixed-sized string
140 std::vector<char> buf(H5Tget_size(dt) + 1, 0x00);
141 auto err = H5Aread(attr, dt, (void *)(&buf[0]));
142 if (err < 0) throw std::runtime_error("Error in h5_read_attribute: Reading a string from the attribute " + name + " failed");
143 s.append(&buf.front());
144 }
145 }
146
147 void h5_write_attribute_to_key(group g, std::string const &key, std::string const &name, std::string const &s) {
148 // create the variable-sized string datatype and dataspace
149 datatype dt = str_dtype();
150 dataspace dspace = H5Screate(H5S_SCALAR);
151
152 // create the attribute for a given key
153 attribute attr = H5Acreate_by_name(g, key.c_str(), name.c_str(), dt, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
154 if (!attr.is_valid()) throw std::runtime_error("Error in h5_write_attribute_to_key: Creating the attribute " + name + " failed");
155
156 // write the string to the attribute
157 herr_t err = H5Awrite(attr, dt, (void *)(s.c_str()));
158 if (err < 0) throw std::runtime_error("Error in h5_write_attribute_to_key: Writing a string to the attribute " + name + " failed");
159 }
160
161 void h5_read_attribute_from_key(group g, std::string const &key, std::string const &name, std::string &s) {
162 s = "";
163
164 // H5Aexists_by_name fails instead of returning false if the key cannot be resolved, e.g. if it is absent
165 htri_t exists{};
166 H5E_BEGIN_TRY { exists = H5Aexists_by_name(g, key.c_str(), name.c_str(), H5P_DEFAULT); }
167 H5E_END_TRY;
168 if (exists < 0) throw std::runtime_error("Error in h5_read_attribute_from_key: " + key + " cannot be resolved in the group " + g.name());
169
170 // return an empty string if the attribute is not present
171 if (exists == 0) return;
172
173 // open the attribute and get dataspace and datatype information
174 attribute attr = H5Aopen_by_name(g, key.c_str(), name.c_str(), H5P_DEFAULT, H5P_DEFAULT);
175 dataspace dspace = H5Aget_space(attr);
176 int rank = H5Sget_simple_extent_ndims(dspace);
177 if (rank != 0) throw std::runtime_error("Error in h5_read_attribute_to_key: Reading a string from a dataspace with rank != 0 is not possible");
178
179 datatype dt = H5Aget_type(attr);
180 H5_ASSERT(H5Tget_class(dt) == H5T_STRING);
181
182 // variable-sized string
183 if (H5Tis_variable_str(dt)) {
184 // first read into a char* pointer, then copy into the string
185 std::array<char *, 1> rd_ptr{nullptr};
186 auto err = H5Aread(attr, dt, (void *)rd_ptr.data());
187 if (err < 0) throw std::runtime_error("Error in h5_read_attribute_to_key: Reading a string from the attribute " + name + " failed");
188 s.append(rd_ptr[0]);
189
190 // free the resources allocated in the variable-length read
191 err = H5Dvlen_reclaim(dt, dspace, H5P_DEFAULT, (void *)rd_ptr.data());
192 if (err < 0) throw std::runtime_error("Error in h5_read_attribute_to_key: Rreeing resources after reading a variable-length string failed");
193 } else { // fixed-sized string
194 std::vector<char> buf(H5Tget_size(dt) + 1, 0x00);
195 auto err = H5Aread(attr, dt, &buf[0]);
196 if (err < 0) throw std::runtime_error("Error in h5_read_attribute_to_key: Reading a string from the attribute " + name + " failed");
197 s.append(&buf.front());
198 }
199 }
200
201 // HDF5 datatype of a char_buf is a fixed-sized string
202 datatype char_buf::dtype() const { return str_dtype(lengths.back()); }
203
204 // dataspace is an n-dimensional array of fixed-sized strings, each of length max_length + 1
206 dataspace ds = H5Screate_simple(static_cast<int>(lengths.size()) - 1, lengths.data(), nullptr);
207 if (!ds.is_valid()) throw make_runtime_error("Error in h5::char_buf: Creating the dataspace for the char_buf failed");
208 return ds;
209 }
210
211 void h5_write(group g, std::string const &name, char_buf const &cb) {
212 // create the dataset for the char_buf
213 auto dt = cb.dtype();
214 auto dspace = cb.dspace();
215 dataset ds = g.create_dataset(name, dt, dspace);
216
217 // write to the dataset
218 auto err = H5Dwrite(ds, dt, dspace, H5S_ALL, H5P_DEFAULT, (void *)cb.buffer.data());
219 if (err < 0) throw make_runtime_error("Error in h5_write: Writing a char_buf to the dataset ", name, " in the group ", g.name(), " failed");
220 }
221
222 void h5_read(group g, std::string const &name, char_buf &cb) {
223 // open the dataset and get dataspace and datatype information
224 dataset ds = g.open_dataset(name);
225 dataspace dspace = H5Dget_space(ds);
226 datatype ty = H5Dget_type(ds);
227
228 // prepare the char_buf to be read into
229 char_buf cb_out;
230 // number of strings
231 int dim = H5Sget_simple_extent_ndims(dspace);
232 cb_out.lengths.resize(dim);
233 H5Sget_simple_extent_dims(dspace, cb_out.lengths.data(), nullptr);
234 // max. length of the strings + 1
235 size_t size = H5Tget_size(ty);
236 cb_out.lengths.push_back(size);
237 // resize the buffer
238 long ltot = std::accumulate(cb_out.lengths.begin(), cb_out.lengths.end(), 1, std::multiplies<>());
239 cb_out.buffer.resize(std::max(ltot, 1l), 0x00);
240
241 // read into the buffer
242 H5_ASSERT(hdf5_type_equal(ty, cb_out.dtype()));
243 auto err = H5Dread(ds, ty, cb_out.dspace(), H5S_ALL, H5P_DEFAULT, (void *)cb_out.buffer.data());
244 if (err < 0) throw make_runtime_error("Error in h5_read: Reading a char_buf from the dataset ", name, " in the group ", g.name(), " failed");
245
246 // move to output char_buf
247 cb = std::move(cb_out);
248 }
249
250 void h5_write_attribute(object obj, std::string const &name, char_buf const &cb) {
251 // datatype and dataspace of char_buf
252 auto dt = cb.dtype();
253 auto dspace = cb.dspace();
254
255 // create the attribute
256 attribute attr = H5Acreate2(obj, name.c_str(), dt, dspace, H5P_DEFAULT, H5P_DEFAULT);
257 if (!attr.is_valid()) throw make_runtime_error("Error in h5_write_attribute: Creating the attribute ", name, " failed");
258
259 // write the char_buf to the attribute
260 herr_t status = H5Awrite(attr, dt, (void *)cb.buffer.data());
261 if (status < 0) throw make_runtime_error("Error in h5_write_attribute: Writing a char_buf to the attribute ", name, " failed");
262 }
263
264 void h5_read_attribute(object obj, std::string const &name, char_buf &cb) {
265 // open the attribute and get dataspace and datatype information
266 attribute attr = H5Aopen(obj, name.c_str(), H5P_DEFAULT);
267 if (!attr.is_valid()) throw make_runtime_error("Error in h5_read_attribute: Opening the attribute ", name, " failed");
268
269 dataspace d_space = H5Aget_space(attr);
270 datatype ty = H5Aget_type(attr);
271
272 // prepare the char_buf to be read into
273 char_buf cb_out;
274 // number of strings
275 int dim = H5Sget_simple_extent_ndims(d_space);
276 cb_out.lengths.resize(dim);
277 H5Sget_simple_extent_dims(d_space, cb_out.lengths.data(), nullptr);
278 // max. length of the strings + 1
279 size_t size = H5Tget_size(ty);
280 cb_out.lengths.push_back(size);
281 // resize the buffer
282 long ltot = std::accumulate(cb_out.lengths.begin(), cb_out.lengths.end(), 1, std::multiplies<>());
283 cb_out.buffer.resize(std::max(ltot, 1l), 0x00);
284
285 // read into the buffer
286 H5_ASSERT(hdf5_type_equal(ty, cb_out.dtype()));
287 auto err = H5Aread(attr, ty, (void *)cb_out.buffer.data());
288 if (err < 0) throw make_runtime_error("Error in h5_read_attribute: Reading a char_buf from the attribute ", name, " failed");
289
290 // move to output char_buf
291 cb = std::move(cb_out);
292 }
293
294} // namespace h5
A handle to an HDF5 group.
Definition group.hpp:44
dataset create_dataset(std::string const &key, datatype ty, dataspace sp, hid_t pl) const
Create a dataset with the given key, datatype, dataspace and dataset creation property list in this g...
Definition group.cpp:157
dataset open_dataset(std::string const &key) const
Open a dataset with the given key in the group.
Definition group.cpp:147
std::string name() const
Get the name of the group.
Definition group.cpp:39
bool is_valid() const
Ensure that the wrapped HDF5 ID is valid (by calling H5Iis_valid).
Definition object.cpp:117
object datatype
Type alias for an HDF5 datatype.
Definition object.hpp:123
object attribute
Type alias for an HDF5 attribute.
Definition object.hpp:132
object dataset
Type alias for an HDF5 dataset.
Definition object.hpp:120
object dataspace
Type alias for an HDF5 dataspace.
Definition object.hpp:126
bool hdf5_type_equal(datatype dt1, datatype dt2)
Check if two HDF5 datatypes are equal.
Definition object.cpp:204
T h5_read(group g, std::string const &key)
Generic implementation for reading from an HDF5 dataset/subgroup.
Definition generic.hpp:53
T h5_read_attribute_from_key(group g, std::string const &key, std::string const &name)
Generic implementation for reading an HDF5 attribute.
Definition generic.hpp:191
T h5_read_attribute(object obj, std::string const &name)
Generic implementation for reading an HDF5 attribute.
Definition generic.hpp:125
void h5_write(group g, std::string const &name, T const &x)
Write a scalar to an HDF5 dataset.
Definition scalar.hpp:71
void h5_write_attribute_to_key(group g, std::string const &key, std::string const &name, std::string const &s)
Write a std::string to an HDF5 attribute.
Definition string.cpp:147
void h5_write_attribute(object, std::string const &, std::string const &)
Write a std::string to an HDF5 attribute.
Definition string.cpp:99
std::runtime_error make_runtime_error(Ts const &...ts)
Create a std::runtime_error with an error message constructed from the given arguments.
Definition utils.hpp:69
Macros used in the h5 library.
Provides functions to read/write std::string, char* and h5::char_buf objects from/to HDF5.
Stores an arbitrary number of strings in a 1-dimensional std::vector<char>.
Definition string.hpp:162
dataspace dspace() const
Get the HDF5 dataspace.
Definition string.cpp:205
datatype dtype() const
Get the HDF5 datatype.
Definition string.cpp:202
v_t lengths
Stores the number of strings in each dimension and the max. allowed length of the strings + 1.
Definition string.hpp:167
std::vector< char > buffer
Stores strings in a 1-dimensional vector.
Definition string.hpp:164
Provides some utility functions for h5.