h5gt 0.2.0
C++ wrapper for HDF5 library (based on HighFive project)
Loading...
Searching...
No Matches
H5Converter_misc.hpp
1/*
2 * Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
3 *
4 * Distributed under the Boost Software License, Version 1.0.
5 * (See accompanying file LICENSE_1_0.txt or copy at
6 * http://www.boost.org/LICENSE_1_0.txt)
7 *
8 */
9#ifndef H5CONVERTER_MISC_HPP
10#define H5CONVERTER_MISC_HPP
11
12#include <algorithm>
13#include <cassert>
14#include <functional>
15#include <numeric>
16#include <sstream>
17#include <string>
18#include <array>
19#include <type_traits>
20
21#ifdef H5GT_USE_BOOST
22// starting Boost 1.64, serialization header must come before ublas
23#include <boost/serialization/vector.hpp>
24#include <boost/multi_array.hpp>
25#include <boost/numeric/ublas/matrix.hpp>
26#endif
27
28#include <H5Dpublic.h>
29#include <H5Ppublic.h>
30
31#include "../H5Reference.hpp"
32#include "H5Utils.hpp"
33
34namespace h5gt {
35
36namespace details {
37
38inline bool is_1D(const std::vector<size_t>& dims) {
39 return std::count_if(dims.begin(), dims.end(), [](size_t i){ return i > 1; }) < 2;
40}
41
42inline size_t compute_total_size(const std::vector<size_t>& dims) {
43 return std::accumulate(dims.begin(), dims.end(), size_t{1u},
44 std::multiplies<size_t>());
45}
46
47inline void check_dimensions_vector(size_t size_vec, size_t size_dataset,
48 size_t dimension) {
49 if (size_vec != size_dataset) {
50 std::ostringstream ss;
51 ss << "Mismatch between vector size (" << size_vec
52 << ") and dataset size (" << size_dataset;
53 ss << ") on dimension " << dimension;
54 throw DataSetException(ss.str());
55 }
56}
57
58
59// Buffer converters
60// =================
61
62// copy multi dimensional vector in C++ in one C-style multi dimensional buffer
63template <typename T>
64inline void vectors_to_single_buffer(const std::vector<T>& vec_single_dim,
65 const std::vector<size_t>& dims,
66 const size_t current_dim,
67 std::vector<T>& buffer) {
68
69 check_dimensions_vector(vec_single_dim.size(), dims[current_dim], current_dim);
70 buffer.insert(buffer.end(), vec_single_dim.begin(), vec_single_dim.end());
71}
72
73
74template <typename T, typename U = typename inspector<T>::base_type>
75inline void
76vectors_to_single_buffer(const std::vector<T>& vec_multi_dim,
77 const std::vector<size_t>& dims,
78 size_t current_dim,
79 std::vector<U>& buffer) {
80
81 check_dimensions_vector(vec_multi_dim.size(), dims[current_dim], current_dim);
82 for (const auto& it : vec_multi_dim) {
83 vectors_to_single_buffer(it, dims, current_dim + 1, buffer);
84 }
85}
86
87// copy single buffer to multi dimensional vector, following specified dimensions
88template <typename T>
89inline typename std::vector<T>::const_iterator
90single_buffer_to_vectors(typename std::vector<T>::const_iterator begin_buffer,
91 typename std::vector<T>::const_iterator end_buffer,
92 const std::vector<size_t>& dims,
93 const size_t current_dim,
94 std::vector<T>& vec_single_dim) {
95 const auto n_elems = static_cast<long>(dims[current_dim]);
96 const auto end_copy_iter = std::min(begin_buffer + n_elems, end_buffer);
97 vec_single_dim.assign(begin_buffer, end_copy_iter);
98 return end_copy_iter;
99}
100
101template <typename T, typename U = typename inspector<T>::base_type>
102inline typename std::vector<U>::const_iterator
103single_buffer_to_vectors(typename std::vector<U>::const_iterator begin_buffer,
104 typename std::vector<U>::const_iterator end_buffer,
105 const std::vector<size_t>& dims,
106 const size_t current_dim,
107 std::vector<std::vector<T>>& vec_multi_dim) {
108 const size_t n_elems = dims[current_dim];
109 vec_multi_dim.resize(n_elems);
110
111 for (auto& subvec : vec_multi_dim) {
112 begin_buffer = single_buffer_to_vectors(
113 begin_buffer, end_buffer, dims, current_dim + 1, subvec);
114 }
115 return begin_buffer;
116}
117
118
119// DATA CONVERTERS
120// ===============
121
122// apply conversion operations to basic scalar type
123template <typename Scalar, class Enable>
125 inline data_converter(const DataSpace&, const DataType&) noexcept {
126
127 static_assert((std::is_arithmetic<Scalar>::value ||
128 std::is_enum<Scalar>::value ||
129 std::is_same<std::vector<const char*>, Scalar>::value ||
130 std::is_same<std::string, Scalar>::value ||
131 std::is_same<std::complex<float>, Scalar>::value ||
132 std::is_same<std::complex<double>, Scalar>::value),
133 "supported datatype should be an arithmetic value, a "
134 "std::string or a container/array");
135 }
136
137 inline Scalar* transform_read(Scalar& datamem) const noexcept {
138 return &datamem;
139 }
140
141 inline const Scalar* transform_write(const Scalar& datamem) const noexcept {
142 return &datamem;
143 }
144
145 inline void process_result(Scalar&) const noexcept {}
146};
147
148
149// apply conversion operations to the incoming data
150// if they are a cstyle array
151template <typename CArray>
152struct data_converter<CArray,
153 typename std::enable_if<(is_c_array<CArray>::value)>::type> {
154 inline data_converter(const DataSpace&, const DataType&) noexcept {}
155
156 inline CArray& transform_read(CArray& datamem) const noexcept {
157 return datamem;
158 }
159
160 inline const CArray& transform_write(const CArray& datamem) const noexcept {
161 return datamem;
162 }
163
164 inline void process_result(CArray&) const noexcept {}
165};
166
167
168// Generic container converter
169template <typename Container,
170 typename T = typename inspector<Container>::base_type>
172 typedef T value_type;
173
174 inline container_converter(const DataSpace& space, const DataType& type)
175 : _space(space), _type(type) {}
176
177 // Ship (pseudo)1D implementation
178 inline value_type* transform_read(Container& vec) {
179 auto&& dims = _space.getDimensions();
180 if (!is_1D(dims))
181 throw DataSpaceException("Dataset cant be converted to 1D");
182 vec.resize(compute_total_size(dims));
183 return vec.data();
184 }
185
186 inline const value_type* transform_write(const Container& vec) const noexcept {
187 return vec.data();
188 }
189
190 inline void process_result(Container&) noexcept {}
191
192 const DataSpace& _space;
193 const DataType& _type;
194};
195
196// Generic container filled with struct (or class) converter
197template <typename Container, typename T = typename inspector<Container>::base_type>
199 typedef T value_type;
200
201 inline container_of_struct_converter(const DataSpace& space, const DataType& type)
202 : _space(space), _type(type) {}
203
204 inline void getVLenMemberAndOffsets(const DataType& type, std::vector<int>& vlen_members, std::vector<size_t>& member_offsets){
205 int nmembers = H5Tget_nmembers(type.getId());
206 int start_offset = 0;
207 for (int i = 0; i < nmembers; i++){
208 H5T_class_t memberclass = H5Tget_member_class(type.getId(), i);
209 if (memberclass == H5T_STRING){
210 DataType str_type = DataType::FromId(H5Tget_member_type(type.getId(), i), false);
211 if (str_type.isVariableStr()){
212 vlen_members.push_back(member_offsets.size());
213 }
214 } else if (memberclass == H5T_COMPOUND){
215 DataType nested_compound_type = DataType::FromId(H5Tget_member_type(type.getId(), i), false);
216 member_offsets.push_back(start_offset + H5Tget_member_offset(type.getId(), i));
217 getVLenMemberAndOffsets(nested_compound_type, vlen_members, member_offsets);
218 continue;
219 }
220
221 if (i == 0 && member_offsets.size() != 0){
222 start_offset = member_offsets[member_offsets.size()-1];
223 continue;
224 }
225
226 member_offsets.push_back(start_offset + H5Tget_member_offset(type.getId(), i));
227 }
228 }
229
230 // Ship (pseudo)1D implementation
231 inline value_type* transform_read(Container& vec) {
232 auto&& dims = _space.getDimensions();
233 if (!is_1D(dims))
234 throw DataSpaceException("Dataset cant be converted to 1D");
235 vec.resize(compute_total_size(dims));
236 // we must explicitly call destructors as we probably will use placement new (for Compound with string)
237 // https://www.cyberforum.ru/cpp-beginners/thread2924904-page2.html
238 for (size_t i = 0; i < vec.size(); i++){
239 vec[i].~value_type();
240 }
241
242 return vec.data();
243 }
244
245 inline const value_type* transform_write(const Container& vec) const noexcept {
246 return vec.data();
247 }
248
249 inline void process_result(Container& vec) noexcept {
250 if (_type.getClass() != DataTypeClass::Compound)
251 return;
252
253 // check if compound contains variable length string
254 std::vector<int> vlen_members;
255 std::vector<size_t> member_offsets;
256 getVLenMemberAndOffsets(_type, vlen_members, member_offsets);
257
258 if (vlen_members.size() < 1)
259 return; // no variable length string
260
261 size_t nmembers = member_offsets.size();
262 size_t type_size = _type.getSize();
263 char * str_ptr;
264 for (size_t i = 0; i < vec.size(); i++){
265 int ind = 0;
266 for (int member = 0; member < nmembers; member++){
267 if (ind < vlen_members.size() && member == vlen_members[ind]){
268 memcpy(&str_ptr, (char *) vec.data() + member_offsets[member] + type_size*i, sizeof(str_ptr));
269 if (str_ptr) // important or undefined behaviour caused by std::string(char* == nullptr)
270 ::new((char *) vec.data() + member_offsets[member] + type_size*i) std::string(str_ptr); // placement new
271 std::free(str_ptr); // important
272 ind++;
273 }
274 }
275 }
276 }
277
278 const DataSpace& _space;
279 const DataType& _type;
280};
281
282
283// apply conversion for vectors 1D
284template <typename T>
286 std::vector<T>,
287 typename std::enable_if<(
288 std::is_same<T, typename inspector<T>::base_type>::value &&
289 !std::is_same<T, Reference>::value &&
290 !std::is_class<T>::value
291 )>::type>
292 : public container_converter<std::vector<T>> {
293 using container_converter<std::vector<T>>::container_converter;
294};
295
296
297// apply conversion for vectors 1D filled with struct (class)
298template <typename T>
300 std::vector<T>,
301 typename std::enable_if<(
302 std::is_same<T, typename inspector<T>::base_type>::value &&
303 !std::is_same<T, Reference>::value &&
304 std::is_class<T>::value
305 )>::type>
306 : public container_of_struct_converter<std::vector<T>> {
307 using container_of_struct_converter<std::vector<T>>::container_of_struct_converter;
308};
309
310
311// apply conversion to std::array
312template <typename T, std::size_t S>
314 std::array<T, S>,
315 typename std::enable_if<(
316 std::is_same<T, typename inspector<T>::base_type>::value)>::type>
317 : public container_converter<std::array<T, S>>
318{
319inline data_converter(const DataSpace& space, const DataType& type)
321{
322 auto&& dims = space.getDimensions();
323 if (!is_1D(dims)) {
324 throw DataSpaceException("Only 1D std::array supported currently.");
325 }
326 if (compute_total_size(dims) != S) {
327 std::ostringstream ss;
328 ss << "Impossible to pair DataSet with " << compute_total_size(dims)
329 << " elements into an array with " << S << " elements.";
330 throw DataSpaceException(ss.str());
331 }
332}
333
334inline T* transform_read(std::array<T, S>& vec) const noexcept {
335 return vec.data();
336}
337};
338
339
340#ifdef H5GT_USE_BOOST
341// apply conversion to boost multi array
342template <typename T, std::size_t Dims>
343struct data_converter<boost::multi_array<T, Dims>, void>
344 : public container_converter<boost::multi_array<T, Dims>> {
345 using MultiArray = boost::multi_array<T, Dims>;
346 using value_type = typename inspector<T>::base_type;
347 using container_converter<MultiArray>::container_converter;
348
349 inline value_type* transform_read(MultiArray& array) {
350 auto&& dims = this->_space.getDimensions();
351 if (std::equal(dims.begin(), dims.end(), array.shape()) == false) {
352 boost::array<typename MultiArray::index, Dims> ext;
353 std::copy(dims.begin(), dims.end(), ext.begin());
354 array.resize(ext);
355 }
356 return array.data();
357 }
358};
359
360
361// apply conversion to boost matrix ublas
362template <typename T>
363struct data_converter<boost::numeric::ublas::matrix<T>, void>
364 : public container_converter<boost::numeric::ublas::matrix<T>> {
365 using Matrix = boost::numeric::ublas::matrix<T>;
366 using value_type = typename inspector<T>::base_type;
367
368 inline data_converter(const DataSpace& space, const DataType& type) : container_converter<Matrix>(space) {
369 assert(space.getDimensions().size() == 2);
370 }
371
372 inline value_type* transform_read(Matrix& array) {
373 boost::array<std::size_t, 2> sizes = {{array.size1(), array.size2()}};
374 auto&& _dims = this->_space.getDimensions();
375 if (std::equal(_dims.begin(), _dims.end(), sizes.begin()) == false) {
376 array.resize(_dims[0], _dims[1], false);
377 array(0, 0) = 0; // force initialization
378 }
379 return &(array(0, 0));
380 }
381
382 inline const value_type* transform_write(const Matrix& array) const noexcept {
383 return &(array(0, 0));
384 }
385};
386#endif
387
388
389// apply conversion for vectors nested vectors
390template <typename T>
391struct data_converter<std::vector<T>,
392 typename std::enable_if<(is_container<T>::value)>::type> {
393 using value_type = typename inspector<T>::base_type;
394
395 inline data_converter(const DataSpace& space, const DataType& type)
396 : _dims(space.getDimensions()) {}
397
398 inline value_type* transform_read(std::vector<T>&) {
399 _vec_align.resize(compute_total_size(_dims));
400 return _vec_align.data();
401 }
402
403 inline const value_type* transform_write(const std::vector<T>& vec) {
404 _vec_align.reserve(compute_total_size(_dims));
405 vectors_to_single_buffer<T>(vec, _dims, 0, _vec_align);
406 return _vec_align.data();
407 }
408
409 inline void process_result(std::vector<T>& vec) const {
410 single_buffer_to_vectors(
411 _vec_align.cbegin(), _vec_align.cend(), _dims, 0, vec);
412 }
413
414 std::vector<size_t> _dims;
415 std::vector<typename inspector<T>::base_type> _vec_align;
416};
417
418
419// apply conversion to scalar string
420template <>
421struct data_converter<std::string, void> {
422 using value_type = const char*; // char data is const, mutable pointer
423
424 inline data_converter(const DataSpace& space, const DataType& type) noexcept
425 : _c_vec(nullptr)
426 , _space(space) {}
427
428 // create a C vector adapted to HDF5
429 // fill last element with NULL to identify end
430 inline value_type* transform_read(std::string&) noexcept {
431 return &_c_vec;
432 }
433
434 inline const value_type* transform_write(const std::string& str) noexcept {
435 _c_vec = str.c_str();
436 return &_c_vec;
437 }
438
439 inline void process_result(std::string& str) {
440 assert(_c_vec != nullptr);
441 str = std::string(_c_vec);
442
443 if (_c_vec != nullptr) {
445 (void)H5Dvlen_reclaim(str_type.getId(false), _space.getId(false), H5P_DEFAULT,
446 &_c_vec);
447 }
448 }
449
450 value_type _c_vec;
451 const DataSpace& _space;
452};
453
454// apply conversion for vectors of string (dereference)
455template <>
456struct data_converter<std::vector<std::string>, void> {
457 using value_type = const char*;
458
459 inline data_converter(const DataSpace& space, const DataType& type) noexcept
460 : _space(space) {}
461
462 // create a C vector adapted to HDF5
463 // fill last element with NULL to identify end
464 inline value_type* transform_read(std::vector<std::string>&) {
465 _c_vec.resize(_space.getDimensions()[0], NULL);
466 return _c_vec.data();
467 }
468
469 inline const value_type* transform_write(const std::vector<std::string>& vec) {
470 _c_vec.resize(vec.size() + 1, NULL);
471 std::transform(vec.begin(), vec.end(), _c_vec.begin(),
472 [](const std::string& str){ return str.c_str(); });
473 return _c_vec.data();
474 }
475
476 inline void process_result(std::vector<std::string>& vec) {
477 vec.resize(_c_vec.size());
478 for (size_t i = 0; i < vec.size(); ++i) {
479 vec[i] = std::string(_c_vec[i]);
480 }
481
482 if (_c_vec.empty() == false && _c_vec[0] != NULL) {
484 (void)H5Dvlen_reclaim(str_type.getId(false), _space.getId(false), H5P_DEFAULT,
485 &(_c_vec[0]));
486 }
487 }
488
489 std::vector<value_type> _c_vec;
490 const DataSpace& _space;
491};
492
493
494
495// apply conversion for fixed-string. Implements container interface
496template <std::size_t N>
498 : public container_converter<FixedLenStringArray<N>, char> {
499 using container_converter<FixedLenStringArray<N>, char>::container_converter;
500};
501
502template <>
503struct data_converter<std::vector<Reference>, void> {
504 inline data_converter(const DataSpace& space, const DataType& type)
505 : _dims(space.getDimensions()) {
506 if (!is_1D(_dims)) {
507 throw DataSpaceException("Only 1D std::array supported currently.");
508 }
509 }
510
511 inline hobj_ref_t* transform_read(std::vector<Reference>& vec) {
512 auto total_size = compute_total_size(_dims);
513 _vec_align.resize(total_size);
514 vec.resize(total_size);
515 return _vec_align.data();
516 }
517
518 inline const hobj_ref_t* transform_write(const std::vector<Reference>& vec) {
519 _vec_align.resize(compute_total_size(_dims));
520 for (size_t i = 0; i < vec.size(); ++i) {
521 vec[i].create_ref(&_vec_align[i]);
522 }
523 return _vec_align.data();
524 }
525
526 inline void process_result(std::vector<Reference>& vec) const {
527 auto* href = const_cast<hobj_ref_t*>(_vec_align.data());
528 for (auto& ref : vec) {
529 ref = Reference(*(href++));
530 }
531 }
532
533 std::vector<size_t> _dims;
534 std::vector<typename inspector<hobj_ref_t>::base_type> _vec_align;
535};
536
537
538} // namespace details
539
540} // namespace h5gt
541
542#ifdef H5GT_USE_EIGEN
543#include "H5ConverterEigen_misc.hpp"
544#endif
545
546#endif // H5CONVERTER_MISC_HPP
create an HDF5 DataType from a C++ type
Definition H5DataType.hpp:124
Exception specific to h5gt DataSpace interface.
Definition H5Exception.hpp:99
Class representing the space (dimensions) of a dataset.
Definition H5DataSpace.hpp:37
std::vector< size_t > getDimensions() const
getDimensions
Definition H5Dataspace_misc.hpp:103
HDF5 Data Type.
Definition H5DataType.hpp:48
size_t getSize() const
Returns the length (in bytes) of this type elements.
Definition H5DataType_misc.hpp:36
DataTypeClass getClass() const
Return the fundamental type.
Definition H5DataType_misc.hpp:32
bool isVariableStr() const
Returns whether the type is a variable-length string.
Definition H5DataType_misc.hpp:55
A structure representing a set of fixed-length strings.
Definition H5DataType.hpp:305
hid_t getId(const bool &increaseRefCount=false) const noexcept
getId
Definition H5Object_misc.hpp:172
An HDF5 (object) reference type.
Definition H5Reference.hpp:31
Definition H5Converter_misc.hpp:171
Definition H5Converter_misc.hpp:198
Definition H5Converter_misc.hpp:124