HighFive 2.9.0
HighFive - Header-only C++ HDF5 interface
Loading...
Searching...
No Matches
H5Inspector_misc.hpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Blue Brain Project
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
10#pragma once
11
12#include <type_traits>
13#include <cstring>
14#include <cassert>
15#include <vector>
16#include <array>
17#include <string>
18#include <numeric>
19
20#include "../H5Reference.hpp"
21
22#include "string_padding.hpp"
23
24#include "H5Inspector_decl.hpp"
25
26
27namespace HighFive {
28namespace details {
29
30inline bool checkDimensions(const std::vector<size_t>& dims, size_t n_dim_requested) {
31 size_t n_dim_actual = dims.size();
32
33 // We should allow reading scalar from shapes like `(1, 1, 1)`.
34 if (n_dim_requested == 0) {
35 if (n_dim_actual == 0ul) {
36 return true;
37 }
38
39 return size_t(std::count(dims.begin(), dims.end(), 1ul)) == n_dim_actual;
40 }
41
42 // For non-scalar datasets, we can squeeze away singleton dimension, but
43 // we never add any.
44 if (n_dim_actual < n_dim_requested) {
45 return false;
46 }
47
48 // Special case for 1-dimensional arrays, which can squeeze `1`s from either
49 // side simultaneously if needed.
50 if (n_dim_requested == 1ul) {
51 return n_dim_actual >= 1ul &&
52 size_t(std::count(dims.begin(), dims.end(), 1ul)) >= n_dim_actual - 1ul;
53 }
54
55 // All other cases strip front only. This avoid unstable behaviour when
56 // squeezing singleton dimensions.
57 size_t n_dim_excess = n_dim_actual - n_dim_requested;
58
59 bool squeeze_back = true;
60 for (size_t i = 1; i <= n_dim_excess; ++i) {
61 if (dims[n_dim_actual - i] != 1) {
62 squeeze_back = false;
63 break;
64 }
65 }
66
67 return squeeze_back;
68}
69
70
71inline std::vector<size_t> squeezeDimensions(const std::vector<size_t>& dims,
72 size_t n_dim_requested) {
73 auto format_error_message = [&]() -> std::string {
74 return "Can't interpret dims = " + format_vector(dims) + " as " +
75 std::to_string(n_dim_requested) + "-dimensional.";
76 };
77
78 if (n_dim_requested == 0) {
79 if (!checkDimensions(dims, n_dim_requested)) {
80 throw std::invalid_argument("Failed dimensions check: " + format_error_message());
81 }
82
83 return {1ul};
84 }
85
86 auto n_dim = dims.size();
87 if (n_dim < n_dim_requested) {
88 throw std::invalid_argument("Failed 'n_dim < n_dim_requested: " + format_error_message());
89 }
90
91 if (n_dim_requested == 1ul) {
92 size_t non_singleton_dim = size_t(-1);
93 for (size_t i = 0; i < n_dim; ++i) {
94 if (dims[i] != 1ul) {
95 if (non_singleton_dim == size_t(-1)) {
96 non_singleton_dim = i;
97 } else {
98 throw std::invalid_argument("Failed one-dimensional: " +
99 format_error_message());
100 }
101 }
102 }
103
104 return {dims[std::min(non_singleton_dim, n_dim - 1)]};
105 }
106
107 size_t n_dim_excess = dims.size() - n_dim_requested;
108 for (size_t i = 1; i <= n_dim_excess; ++i) {
109 if (dims[n_dim - i] != 1) {
110 throw std::invalid_argument("Failed stripping from back:" + format_error_message());
111 }
112 }
113
114 return std::vector<size_t>(dims.begin(),
115 dims.end() - static_cast<std::ptrdiff_t>(n_dim_excess));
116}
117} // namespace details
118
119
120/*****
121inspector<T> {
122 using type = T
123 // base_type is the base type inside c++ (e.g. std::vector<int> => int)
124 using base_type
125 // hdf5_type is the base read by hdf5 (c-type) (e.g. std::vector<std::string> => const char*)
126 using hdf5_type
127
128 // Number of dimensions starting from here
129 static constexpr size_t recursive_ndim
130 // Is the inner type trivially copyable for optimisation
131 // If this value is true: data() is mandatory
132 // If this value is false: getSizeVal, getSize, serialize, unserialize are mandatory
133 static constexpr bool is_trivially_copyable
134
135 // Reading:
136 // Allocate the value following dims (should be recursive)
137 static void prepare(type& val, const std::vector<std::size_t> dims)
138 // Return the size of the vector pass to/from hdf5 from a vector of dims
139 static size_t getSize(const std::vector<size_t>& dims)
140 // Return a pointer of the first value of val (for reading)
141 static hdf5_type* data(type& val)
142 // Take a serialized vector 'in', some dims and copy value to val (for reading)
143 static void unserialize(const hdf5_type* in, const std::vector<size_t>&i, type& val)
144
145
146 // Writing:
147 // Return the size of the vector pass to/from hdf5 from a value
148 static size_t getSizeVal(const type& val)
149 // Return a point of the first value of val
150 static const hdf5_type* data(const type& val)
151 // Take a val and serialize it inside 'out'
152 static void serialize(const type& val, hdf5_type* out)
153 // Return an array of dimensions of the space needed for writing val
154 static std::vector<size_t> getDimensions(const type& val)
155}
156*****/
157
158
159namespace details {
160template <typename T>
161struct type_helper {
162 using type = unqualified_t<T>;
163 using base_type = unqualified_t<T>;
164 using hdf5_type = base_type;
165
166 static constexpr size_t ndim = 0;
167 static constexpr size_t recursive_ndim = ndim;
168 static constexpr bool is_trivially_copyable = std::is_trivially_copyable<type>::value;
169
170 static std::vector<size_t> getDimensions(const type& /* val */) {
171 return {};
172 }
173
174 static size_t getSizeVal(const type& val) {
175 return compute_total_size(getDimensions(val));
176 }
177
178 static size_t getSize(const std::vector<size_t>& dims) {
179 return compute_total_size(dims);
180 }
181
182 static void prepare(type& /* val */, const std::vector<size_t>& /* dims */) {}
183
184 static hdf5_type* data(type& val) {
185 static_assert(is_trivially_copyable, "The type is not trivially copyable");
186 return &val;
187 }
188
189 static const hdf5_type* data(const type& val) {
190 static_assert(is_trivially_copyable, "The type is not trivially copyable");
191 return &val;
192 }
193
194 static void serialize(const type& val, hdf5_type* m) {
195 static_assert(is_trivially_copyable, "The type is not trivially copyable");
196 *m = val;
197 }
198
199 static void unserialize(const hdf5_type* vec,
200 const std::vector<size_t>& /* dims */,
201 type& val) {
202 static_assert(is_trivially_copyable, "The type is not trivially copyable");
203 val = vec[0];
204 }
205};
206
207template <typename T>
208struct inspector: type_helper<T> {};
209
210enum class Boolean : int8_t {
211 HighFiveFalse = 0,
212 HighFiveTrue = 1,
213};
214
215template <>
216struct inspector<bool>: type_helper<bool> {
217 using base_type = Boolean;
218 using hdf5_type = int8_t;
219
220 static constexpr bool is_trivially_copyable = false;
221
222 static hdf5_type* data(type& /* val */) {
223 throw DataSpaceException("A boolean cannot be read directly.");
224 }
225
226 static const hdf5_type* data(const type& /* val */) {
227 throw DataSpaceException("A boolean cannot be written directly.");
228 }
229
230 static void unserialize(const hdf5_type* vec,
231 const std::vector<size_t>& /* dims */,
232 type& val) {
233 val = vec[0] != 0 ? true : false;
234 }
235
236 static void serialize(const type& val, hdf5_type* m) {
237 *m = val ? 1 : 0;
238 }
239};
240
241template <>
242struct inspector<std::string>: type_helper<std::string> {
243 using hdf5_type = const char*;
244
245 static hdf5_type* data(type& /* val */) {
246 throw DataSpaceException("A std::string cannot be read directly.");
247 }
248
249 static const hdf5_type* data(const type& /* val */) {
250 throw DataSpaceException("A std::string cannot be written directly.");
251 }
252
253 template <class It>
254 static void serialize(const type& val, It m) {
255 (*m).assign(val.data(), val.size(), StringPadding::NullTerminated);
256 }
257
258 template <class It>
259 static void unserialize(const It& vec, const std::vector<size_t>& /* dims */, type& val) {
260 const auto& view = *vec;
261 val.assign(view.data(), view.length());
262 }
263};
264
265template <>
266struct inspector<Reference>: type_helper<Reference> {
267 using hdf5_type = hobj_ref_t;
268
269 static constexpr bool is_trivially_copyable = false;
270
271 static hdf5_type* data(type& /* val */) {
272 throw DataSpaceException("A Reference cannot be read directly.");
273 }
274
275 static const hdf5_type* data(const type& /* val */) {
276 throw DataSpaceException("A Reference cannot be written directly.");
277 }
278
279 static void serialize(const type& val, hdf5_type* m) {
280 hobj_ref_t ref;
281 val.create_ref(&ref);
282 *m = ref;
283 }
284
285 static void unserialize(const hdf5_type* vec,
286 const std::vector<size_t>& /* dims */,
287 type& val) {
288 val = type{vec[0]};
289 }
290};
291
292template <size_t N>
293struct inspector<deprecated::FixedLenStringArray<N>> {
294 using type = deprecated::FixedLenStringArray<N>;
295 using value_type = char*;
296 using base_type = deprecated::FixedLenStringArray<N>;
297 using hdf5_type = char;
298
299 static constexpr size_t ndim = 1;
300 static constexpr size_t recursive_ndim = ndim;
301 static constexpr bool is_trivially_copyable = false;
302
303 static std::vector<size_t> getDimensions(const type& val) {
304 return std::vector<size_t>{val.size()};
305 }
306
307 static size_t getSizeVal(const type& val) {
308 return N * compute_total_size(getDimensions(val));
309 }
310
311 static size_t getSize(const std::vector<size_t>& dims) {
312 return N * compute_total_size(dims);
313 }
314
315 static void prepare(type& /* val */, const std::vector<size_t>& dims) {
316 if (dims[0] > N) {
317 std::ostringstream os;
318 os << "Size of FixedlenStringArray (" << N << ") is too small for dims (" << dims[0]
319 << ").";
320 throw DataSpaceException(os.str());
321 }
322 }
323
324 static hdf5_type* data(type& val) {
325 return val.data();
326 }
327
328 static const hdf5_type* data(const type& val) {
329 return val.data();
330 }
331
332 static void serialize(const type& val, hdf5_type* m) {
333 for (size_t i = 0; i < val.size(); ++i) {
334 std::memcpy(m + i * N, val[i], N);
335 }
336 }
337
338 static void unserialize(const hdf5_type* vec, const std::vector<size_t>& dims, type& val) {
339 for (size_t i = 0; i < dims[0]; ++i) {
340 std::array<char, N> s;
341 std::memcpy(s.data(), vec + (i * N), N);
342 val.push_back(s);
343 }
344 }
345};
346
347template <typename T>
348struct inspector<std::vector<T>> {
349 using type = std::vector<T>;
350 using value_type = unqualified_t<T>;
351 using base_type = typename inspector<value_type>::base_type;
352 using hdf5_type = typename inspector<value_type>::hdf5_type;
353
354 static constexpr size_t ndim = 1;
355 static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
356 static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
357 inspector<value_type>::is_trivially_copyable;
358
359 static std::vector<size_t> getDimensions(const type& val) {
360 std::vector<size_t> sizes(recursive_ndim, 1ul);
361 sizes[0] = val.size();
362 if (!val.empty()) {
363 auto s = inspector<value_type>::getDimensions(val[0]);
364 assert(s.size() + ndim == sizes.size());
365 for (size_t i = 0; i < s.size(); ++i) {
366 sizes[i + ndim] = s[i];
367 }
368 }
369 return sizes;
370 }
371
372 static size_t getSizeVal(const type& val) {
373 return compute_total_size(getDimensions(val));
374 }
375
376 static size_t getSize(const std::vector<size_t>& dims) {
377 return compute_total_size(dims);
378 }
379
380 static void prepare(type& val, const std::vector<size_t>& dims) {
381 val.resize(dims[0]);
382 std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
383 for (auto&& e: val) {
384 inspector<value_type>::prepare(e, next_dims);
385 }
386 }
387
388 static hdf5_type* data(type& val) {
389 return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
390 }
391
392 static const hdf5_type* data(const type& val) {
393 return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
394 }
395
396 template <class It>
397 static void serialize(const type& val, It m) {
398 if (!val.empty()) {
399 size_t subsize = inspector<value_type>::getSizeVal(val[0]);
400 for (auto&& e: val) {
401 inspector<value_type>::serialize(e, m);
402 m += subsize;
403 }
404 }
405 }
406
407 template <class It>
408 static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
409 std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
410 size_t next_size = compute_total_size(next_dims);
411 for (size_t i = 0; i < dims[0]; ++i) {
412 inspector<value_type>::unserialize(vec_align + i * next_size, next_dims, val[i]);
413 }
414 }
415};
416
417template <>
418struct inspector<std::vector<bool>> {
419 using type = std::vector<bool>;
420 using value_type = bool;
421 using base_type = Boolean;
422 using hdf5_type = uint8_t;
423
424 static constexpr size_t ndim = 1;
425 static constexpr size_t recursive_ndim = ndim;
426 static constexpr bool is_trivially_copyable = false;
427
428 static std::vector<size_t> getDimensions(const type& val) {
429 std::vector<size_t> sizes{val.size()};
430 return sizes;
431 }
432
433 static size_t getSizeVal(const type& val) {
434 return val.size();
435 }
436
437 static size_t getSize(const std::vector<size_t>& dims) {
438 if (dims.size() > 1) {
439 throw DataSpaceException("std::vector<bool> is only 1 dimension.");
440 }
441 return dims[0];
442 }
443
444 static void prepare(type& val, const std::vector<size_t>& dims) {
445 if (dims.size() > 1) {
446 throw DataSpaceException("std::vector<bool> is only 1 dimension.");
447 }
448 val.resize(dims[0]);
449 }
450
451 static hdf5_type* data(type& /* val */) {
452 throw DataSpaceException("A std::vector<bool> cannot be read directly.");
453 }
454
455 static const hdf5_type* data(const type& /* val */) {
456 throw DataSpaceException("A std::vector<bool> cannot be written directly.");
457 }
458
459 static void serialize(const type& val, hdf5_type* m) {
460 for (size_t i = 0; i < val.size(); ++i) {
461 m[i] = val[i] ? 1 : 0;
462 }
463 }
464
465 static void unserialize(const hdf5_type* vec_align,
466 const std::vector<size_t>& dims,
467 type& val) {
468 for (size_t i = 0; i < dims[0]; ++i) {
469 val[i] = vec_align[i] != 0 ? true : false;
470 }
471 }
472};
473
474template <typename T, size_t N>
475struct inspector<std::array<T, N>> {
476 using type = std::array<T, N>;
477 using value_type = unqualified_t<T>;
478 using base_type = typename inspector<value_type>::base_type;
479 using hdf5_type = typename inspector<value_type>::hdf5_type;
480
481 static constexpr size_t ndim = 1;
482 static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
483 static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
484 sizeof(type) == N * sizeof(T) &&
485 inspector<value_type>::is_trivially_copyable;
486
487 static std::vector<size_t> getDimensions(const type& val) {
488 std::vector<size_t> sizes{N};
489 if (!val.empty()) {
490 auto s = inspector<value_type>::getDimensions(val[0]);
491 sizes.insert(sizes.end(), s.begin(), s.end());
492 }
493 return sizes;
494 }
495
496 static size_t getSizeVal(const type& val) {
497 return compute_total_size(getDimensions(val));
498 }
499
500 static size_t getSize(const std::vector<size_t>& dims) {
501 return compute_total_size(dims);
502 }
503
504 static void prepare(type& val, const std::vector<size_t>& dims) {
505 if (dims[0] > N) {
506 std::ostringstream os;
507 os << "Size of std::array (" << N << ") is too small for dims (" << dims[0] << ").";
508 throw DataSpaceException(os.str());
509 }
510
511 std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
512 for (auto&& e: val) {
513 inspector<value_type>::prepare(e, next_dims);
514 }
515 }
516
517 static hdf5_type* data(type& val) {
518 return inspector<value_type>::data(val[0]);
519 }
520
521 static const hdf5_type* data(const type& val) {
522 return inspector<value_type>::data(val[0]);
523 }
524
525 template <class It>
526 static void serialize(const type& val, It m) {
527 size_t subsize = inspector<value_type>::getSizeVal(val[0]);
528 for (auto& e: val) {
529 inspector<value_type>::serialize(e, m);
530 m += subsize;
531 }
532 }
533
534 template <class It>
535 static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
536 if (dims[0] != N) {
537 std::ostringstream os;
538 os << "Impossible to pair DataSet with " << dims[0] << " elements into an array with "
539 << N << " elements.";
540 throw DataSpaceException(os.str());
541 }
542 std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
543 size_t next_size = compute_total_size(next_dims);
544 for (size_t i = 0; i < dims[0]; ++i) {
545 inspector<value_type>::unserialize(vec_align + i * next_size, next_dims, val[i]);
546 }
547 }
548};
549
550// Cannot be use for reading
551template <typename T>
552struct inspector<T*> {
553 using type = T*;
554 using value_type = unqualified_t<T>;
555 using base_type = typename inspector<value_type>::base_type;
556 using hdf5_type = typename inspector<value_type>::hdf5_type;
557
558 static constexpr size_t ndim = 1;
559 static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
560 static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
561 inspector<value_type>::is_trivially_copyable;
562
563 static size_t getSizeVal(const type& /* val */) {
564 throw DataSpaceException("Not possible to have size of a T*");
565 }
566
567 static std::vector<size_t> getDimensions(const type& /* val */) {
568 throw DataSpaceException("Not possible to have size of a T*");
569 }
570
571 static const hdf5_type* data(const type& val) {
572 return reinterpret_cast<const hdf5_type*>(val);
573 }
574
575 /* it works because there is only T[][][] currently
576 we will fix it one day */
577 static void serialize(const type& /* val */, hdf5_type* /* m */) {
578 throw DataSpaceException("Not possible to serialize a T*");
579 }
580};
581
582// Cannot be use for reading
583template <typename T, size_t N>
584struct inspector<T[N]> {
585 using type = T[N];
586 using value_type = unqualified_t<T>;
587 using base_type = typename inspector<value_type>::base_type;
588 using hdf5_type = typename inspector<value_type>::hdf5_type;
589
590 static constexpr size_t ndim = 1;
591 static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
592 static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
593 inspector<value_type>::is_trivially_copyable;
594
595 static void prepare(type& val, const std::vector<size_t>& dims) {
596 if (dims.size() < 1) {
597 throw DataSpaceException("Invalid 'dims', must be at least 1 dimensional.");
598 }
599
600 if (dims[0] != N) {
601 throw DataSpaceException("Dimensions mismatch.");
602 }
603
604 std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
605 for (size_t i = 0; i < dims[0]; ++i) {
606 inspector<value_type>::prepare(val[i], next_dims);
607 }
608 }
609
610 static size_t getSizeVal(const type& val) {
611 return compute_total_size(getDimensions(val));
612 }
613
614 static std::vector<size_t> getDimensions(const type& val) {
615 std::vector<size_t> sizes{N};
616 if (N > 0) {
617 auto s = inspector<value_type>::getDimensions(val[0]);
618 sizes.insert(sizes.end(), s.begin(), s.end());
619 }
620 return sizes;
621 }
622
623 static const hdf5_type* data(const type& val) {
624 return inspector<value_type>::data(val[0]);
625 }
626
627 static hdf5_type* data(type& val) {
628 return inspector<value_type>::data(val[0]);
629 }
630
631 /* it works because there is only T[][][] currently
632 we will fix it one day */
633 static void serialize(const type& val, hdf5_type* m) {
634 size_t subsize = inspector<value_type>::getSizeVal(val[0]);
635 for (size_t i = 0; i < N; ++i) {
636 inspector<value_type>::serialize(val[i], m + i * subsize);
637 }
638 }
639};
640
641} // namespace details
642} // namespace HighFive
643
644#ifdef H5_USE_BOOST
645#include <highfive/boost.hpp>
646#endif
647
648#ifdef H5_USE_EIGEN
649#include <highfive/eigen.hpp>
650#endif
size_t getSize(const File &file, const std::string &path)
Get the size of an existing DataSet in an open HDF5 file.
Definition H5Easy_public.hpp:82
Definition H5_definitions.hpp:22
size_t compute_total_size(const std::vector< size_t > &dims)
Definition H5Inspector_decl.hpp:10