HighFive  2.2.2
HighFive - Header-only C++ HDF5 interface
H5Converter_misc.hpp
Go to the documentation of this file.
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 
20 #ifdef H5_USE_BOOST
21 // starting Boost 1.64, serialization header must come before ublas
22 #include <boost/serialization/vector.hpp>
23 #include <boost/multi_array.hpp>
24 #include <boost/numeric/ublas/matrix.hpp>
25 #endif
26 
27 #include <H5Dpublic.h>
28 #include <H5Ppublic.h>
29 
30 #include "../H5Reference.hpp"
31 #include "H5Utils.hpp"
32 
33 namespace HighFive {
34 
35 namespace details {
36 
37 inline bool is_1D(const std::vector<size_t>& dims) {
38  return std::count_if(dims.begin(), dims.end(), [](size_t i){ return i > 1; }) < 2;
39 }
40 
41 inline size_t compute_total_size(const std::vector<size_t>& dims) {
42  return std::accumulate(dims.begin(), dims.end(), size_t{1u},
43  std::multiplies<size_t>());
44 }
45 
46 inline void check_dimensions_vector(size_t size_vec, size_t size_dataset,
47  size_t dimension) {
48  if (size_vec != size_dataset) {
49  std::ostringstream ss;
50  ss << "Mismatch between vector size (" << size_vec
51  << ") and dataset size (" << size_dataset;
52  ss << ") on dimension " << dimension;
53  throw DataSetException(ss.str());
54  }
55 }
56 
57 
58 // Buffer converters
59 // =================
60 
61 // copy multi dimensional vector in C++ in one C-style multi dimensional buffer
62 template <typename T>
63 inline void vectors_to_single_buffer(const std::vector<T>& vec_single_dim,
64  const std::vector<size_t>& dims,
65  const size_t current_dim,
66  std::vector<T>& buffer) {
67 
68  check_dimensions_vector(vec_single_dim.size(), dims[current_dim], current_dim);
69  buffer.insert(buffer.end(), vec_single_dim.begin(), vec_single_dim.end());
70 }
71 
72 
73 template <typename T, typename U = typename type_of_array<T>::type>
74 inline void
75 vectors_to_single_buffer(const std::vector<T>& vec_multi_dim,
76  const std::vector<size_t>& dims,
77  size_t current_dim,
78  std::vector<U>& buffer) {
79 
80  check_dimensions_vector(vec_multi_dim.size(), dims[current_dim], current_dim);
81  for (const auto& it : vec_multi_dim) {
82  vectors_to_single_buffer(it, dims, current_dim + 1, buffer);
83  }
84 }
85 
86 // copy single buffer to multi dimensional vector, following specified dimensions
87 template <typename T>
88 inline typename std::vector<T>::const_iterator
89 single_buffer_to_vectors(typename std::vector<T>::const_iterator begin_buffer,
90  typename std::vector<T>::const_iterator end_buffer,
91  const std::vector<size_t>& dims,
92  const size_t current_dim,
93  std::vector<T>& vec_single_dim) {
94  const auto n_elems = static_cast<long>(dims[current_dim]);
95  const auto end_copy_iter = std::min(begin_buffer + n_elems, end_buffer);
96  vec_single_dim.assign(begin_buffer, end_copy_iter);
97  return end_copy_iter;
98 }
99 
100 template <typename T, typename U = typename type_of_array<T>::type>
101 inline typename std::vector<U>::const_iterator
102 single_buffer_to_vectors(typename std::vector<U>::const_iterator begin_buffer,
103  typename std::vector<U>::const_iterator end_buffer,
104  const std::vector<size_t>& dims,
105  const size_t current_dim,
106  std::vector<std::vector<T>>& vec_multi_dim) {
107  const size_t n_elems = dims[current_dim];
108  vec_multi_dim.resize(n_elems);
109 
110  for (auto& subvec : vec_multi_dim) {
111  begin_buffer = single_buffer_to_vectors(
112  begin_buffer, end_buffer, dims, current_dim + 1, subvec);
113  }
114  return begin_buffer;
115 }
116 
117 
118 // DATA CONVERTERS
119 // ===============
120 
121 // apply conversion operations to basic scalar type
122 template <typename Scalar, class Enable>
123 struct data_converter {
124  inline data_converter(const DataSpace&) noexcept {
125 
126  static_assert((std::is_arithmetic<Scalar>::value ||
127  std::is_enum<Scalar>::value ||
128  std::is_same<std::string, Scalar>::value),
129  "supported datatype should be an arithmetic value, a "
130  "std::string or a container/array");
131  }
132 
133  inline Scalar* transform_read(Scalar& datamem) const noexcept {
134  return &datamem;
135  }
136 
137  inline const Scalar* transform_write(const Scalar& datamem) const noexcept {
138  return &datamem;
139  }
140 
141  inline void process_result(Scalar&) const noexcept {}
142 };
143 
144 
145 // apply conversion operations to the incoming data
146 // if they are a cstyle array
147 template <typename CArray>
148 struct data_converter<CArray,
149  typename std::enable_if<(is_c_array<CArray>::value)>::type> {
150  inline data_converter(const DataSpace&) noexcept {}
151 
152  inline CArray& transform_read(CArray& datamem) const noexcept {
153  return datamem;
154  }
155 
156  inline const CArray& transform_write(const CArray& datamem) const noexcept {
157  return datamem;
158  }
159 
160  inline void process_result(CArray&) const noexcept {}
161 };
162 
163 // Generic container converter
164 template <typename Container, typename T = typename type_of_array<Container>::type>
165 struct container_converter {
166  typedef T value_type;
167 
168  inline container_converter(const DataSpace& space)
169  : _space(space) {}
170 
171  // Ship (pseudo)1D implementation
172  inline value_type* transform_read(Container& vec) const {
173  auto&& dims = _space.getDimensions();
174  if (!is_1D(dims))
175  throw DataSpaceException("Dataset cant be converted to 1D");
176  vec.resize(compute_total_size(dims));
177  return vec.data();
178  }
179 
180  inline const value_type* transform_write(const Container& vec) const noexcept {
181  return vec.data();
182  }
183 
184  inline void process_result(Container&) const noexcept {}
185 
186  const DataSpace& _space;
187 };
188 
189 
190 // apply conversion for vectors 1D
191 template <typename T>
192 struct data_converter<
193  std::vector<T>,
194  typename std::enable_if<(
195  std::is_same<T, typename type_of_array<T>::type>::value &&
196  !std::is_same<T, Reference>::value
197  )>::type>
198  : public container_converter<std::vector<T>> {
199 
200  using container_converter<std::vector<T>>::container_converter;
201 };
202 
203 
204 // apply conversion to std::array
205 template <typename T, std::size_t S>
206 struct data_converter<
207  std::array<T, S>,
208  typename std::enable_if<(
209  std::is_same<T, typename type_of_array<T>::type>::value)>::type>
210  : public container_converter<std::array<T, S>> {
211 
212  inline data_converter(const DataSpace& space)
213  : container_converter<std::array<T, S>>(space)
214  {
215  auto&& dims = space.getDimensions();
216  if (!is_1D(dims)) {
217  throw DataSpaceException("Only 1D std::array supported currently.");
218  }
219  if (compute_total_size(dims) != S) {
220  std::ostringstream ss;
221  ss << "Impossible to pair DataSet with " << compute_total_size(dims)
222  << " elements into an array with " << S << " elements.";
223  throw DataSpaceException(ss.str());
224  }
225  }
226 
227  inline T* transform_read(std::array<T, S>& vec) const noexcept {
228  return vec.data();
229  }
230 };
231 
232 
233 #ifdef H5_USE_BOOST
234 // apply conversion to boost multi array
235 template <typename T, std::size_t Dims>
236 struct data_converter<boost::multi_array<T, Dims>, void>
237  : public container_converter<boost::multi_array<T, Dims>> {
238  using MultiArray = boost::multi_array<T, Dims>;
239  using value_type = typename type_of_array<T>::type;
240  using container_converter<MultiArray>::container_converter;
241 
242  inline value_type* transform_read(MultiArray& array) {
243  auto&& dims = this->_space.getDimensions();
244  if (std::equal(dims.begin(), dims.end(), array.shape()) == false) {
245  boost::array<typename MultiArray::index, Dims> ext;
246  std::copy(dims.begin(), dims.end(), ext.begin());
247  array.resize(ext);
248  }
249  return array.data();
250  }
251 };
252 
253 
254 // apply conversion to boost matrix ublas
255 template <typename T>
256 struct data_converter<boost::numeric::ublas::matrix<T>, void>
257  : public container_converter<boost::numeric::ublas::matrix<T>> {
258  using Matrix = boost::numeric::ublas::matrix<T>;
259  using value_type = typename type_of_array<T>::type;
260 
261  inline data_converter(const DataSpace& space) : container_converter<Matrix>(space) {
262  assert(space.getDimensions().size() == 2);
263  }
264 
265  inline value_type* transform_read(Matrix& array) {
266  boost::array<std::size_t, 2> sizes = {{array.size1(), array.size2()}};
267  auto&& _dims = this->_space.getDimensions();
268  if (std::equal(_dims.begin(), _dims.end(), sizes.begin()) == false) {
269  array.resize(_dims[0], _dims[1], false);
270  array(0, 0) = 0; // force initialization
271  }
272  return &(array(0, 0));
273  }
274 
275  inline const value_type* transform_write(const Matrix& array) const noexcept {
276  return &(array(0, 0));
277  }
278 };
279 #endif
280 
281 
282 // apply conversion for vectors nested vectors
283 template <typename T>
284 struct data_converter<std::vector<T>,
285  typename std::enable_if<(is_container<T>::value)>::type> {
286  using value_type = typename type_of_array<T>::type;
287 
288  inline data_converter(const DataSpace& space)
289  : _dims(space.getDimensions()) {}
290 
291  inline value_type* transform_read(std::vector<T>&) {
292  _vec_align.resize(compute_total_size(_dims));
293  return _vec_align.data();
294  }
295 
296  inline const value_type* transform_write(const std::vector<T>& vec) {
297  _vec_align.reserve(compute_total_size(_dims));
298  vectors_to_single_buffer<T>(vec, _dims, 0, _vec_align);
299  return _vec_align.data();
300  }
301 
302  inline void process_result(std::vector<T>& vec) const {
303  single_buffer_to_vectors(
304  _vec_align.cbegin(), _vec_align.cend(), _dims, 0, vec);
305  }
306 
307  std::vector<size_t> _dims;
308  std::vector<typename type_of_array<T>::type> _vec_align;
309 };
310 
311 
312 // apply conversion to scalar string
313 template <>
314 struct data_converter<std::string, void> {
315  using value_type = const char*; // char data is const, mutable pointer
316 
317  inline data_converter(const DataSpace& space) noexcept
318  : _c_vec(nullptr)
319  , _space(space) {}
320 
321  // create a C vector adapted to HDF5
322  // fill last element with NULL to identify end
323  inline value_type* transform_read(std::string&) noexcept {
324  return &_c_vec;
325  }
326 
327  inline const value_type* transform_write(const std::string& str) noexcept {
328  _c_vec = str.c_str();
329  return &_c_vec;
330  }
331 
332  inline void process_result(std::string& str) {
333  assert(_c_vec != nullptr);
334  str = std::string(_c_vec);
335 
336  if (_c_vec != nullptr) {
337  AtomicType<std::string> str_type;
338  (void)H5Dvlen_reclaim(str_type.getId(), _space.getId(), H5P_DEFAULT,
339  &_c_vec);
340  }
341  }
342 
343  value_type _c_vec;
344  const DataSpace& _space;
345 };
346 
347 // apply conversion for vectors of string (dereference)
348 template <>
349 struct data_converter<std::vector<std::string>, void> {
350  using value_type = const char*;
351 
352  inline data_converter(const DataSpace& space) noexcept
353  : _space(space) {}
354 
355  // create a C vector adapted to HDF5
356  // fill last element with NULL to identify end
357  inline value_type* transform_read(std::vector<std::string>&) {
358  _c_vec.resize(_space.getDimensions()[0], NULL);
359  return _c_vec.data();
360  }
361 
362  inline const value_type* transform_write(const std::vector<std::string>& vec) {
363  _c_vec.resize(vec.size() + 1, NULL);
364  std::transform(vec.begin(), vec.end(), _c_vec.begin(),
365  [](const std::string& str){ return str.c_str(); });
366  return _c_vec.data();
367  }
368 
369  inline void process_result(std::vector<std::string>& vec) {
370  vec.resize(_c_vec.size());
371  for (size_t i = 0; i < vec.size(); ++i) {
372  vec[i] = std::string(_c_vec[i]);
373  }
374 
375  if (_c_vec.empty() == false && _c_vec[0] != NULL) {
376  AtomicType<std::string> str_type;
377  (void)H5Dvlen_reclaim(str_type.getId(), _space.getId(), H5P_DEFAULT,
378  &(_c_vec[0]));
379  }
380  }
381 
382  std::vector<value_type> _c_vec;
383  const DataSpace& _space;
384 };
385 
386 
387 
388 // apply conversion for fixed-string. Implements container interface
389 template <std::size_t N>
390 struct data_converter<FixedLenStringArray<N>, void>
391  : public container_converter<FixedLenStringArray<N>, char> {
392  using container_converter<FixedLenStringArray<N>, char>::container_converter;
393 };
394 
395 template <>
396 struct data_converter<std::vector<Reference>, void> {
397  inline data_converter(const DataSpace& space)
398  : _dims(space.getDimensions()) {
399  if (!is_1D(_dims)) {
400  throw DataSpaceException("Only 1D std::array supported currently.");
401  }
402  }
403 
404  inline hobj_ref_t* transform_read(std::vector<Reference>& vec) {
405  auto total_size = compute_total_size(_dims);
406  _vec_align.resize(total_size);
407  vec.resize(total_size);
408  return _vec_align.data();
409  }
410 
411  inline const hobj_ref_t* transform_write(const std::vector<Reference>& vec) {
412  _vec_align.reserve(compute_total_size(_dims));
413  for (size_t i = 0; i < vec.size(); ++i) {
414  vec[i].create_ref(&_vec_align[i]);
415  }
416  return _vec_align.data();
417  }
418 
419  inline void process_result(std::vector<Reference>& vec) const {
420  auto* href = const_cast<hobj_ref_t*>(_vec_align.data());
421  for (auto& ref : vec) {
422  ref = Reference(*(href++));
423  }
424  }
425 
426  std::vector<size_t> _dims;
427  std::vector<typename type_of_array<hobj_ref_t>::type> _vec_align;
428 };
429 
430 } // namespace details
431 
432 } // namespace HighFive
433 
434 #ifdef H5_USE_EIGEN
435 #include "H5ConverterEigen_misc.hpp"
436 #endif
437 
438 #endif // H5CONVERTER_MISC_HPP
Definition: H5_definitions.hpp:15