Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 08:55:33

0001 /*
0002  *  Copyright (c) 2022 Blue Brain Project
0003  *
0004  *  Distributed under the Boost Software License, Version 1.0.
0005  *    (See accompanying file LICENSE_1_0.txt or copy at
0006  *          http://www.boost.org/LICENSE_1_0.txt)
0007  *
0008  */
0009 
0010 #pragma once
0011 
0012 #include <type_traits>
0013 #include <cstring>
0014 #include <cassert>
0015 #include <vector>
0016 #include <array>
0017 #include <string>
0018 #include <numeric>
0019 
0020 #include "../H5Reference.hpp"
0021 
0022 #include "string_padding.hpp"
0023 
0024 #include "H5Inspector_decl.hpp"
0025 
0026 
0027 namespace HighFive {
0028 namespace details {
0029 
0030 inline bool checkDimensions(const std::vector<size_t>& dims, size_t n_dim_requested) {
0031     size_t n_dim_actual = dims.size();
0032 
0033     // We should allow reading scalar from shapes like `(1, 1, 1)`.
0034     if (n_dim_requested == 0) {
0035         if (n_dim_actual == 0ul) {
0036             return true;
0037         }
0038 
0039         return size_t(std::count(dims.begin(), dims.end(), 1ul)) == n_dim_actual;
0040     }
0041 
0042     // For non-scalar datasets, we can squeeze away singleton dimension, but
0043     // we never add any.
0044     if (n_dim_actual < n_dim_requested) {
0045         return false;
0046     }
0047 
0048     // Special case for 1-dimensional arrays, which can squeeze `1`s from either
0049     // side simultaneously if needed.
0050     if (n_dim_requested == 1ul) {
0051         return n_dim_actual >= 1ul &&
0052                size_t(std::count(dims.begin(), dims.end(), 1ul)) >= n_dim_actual - 1ul;
0053     }
0054 
0055     // All other cases strip front only. This avoid unstable behaviour when
0056     // squeezing singleton dimensions.
0057     size_t n_dim_excess = n_dim_actual - n_dim_requested;
0058 
0059     bool squeeze_back = true;
0060     for (size_t i = 1; i <= n_dim_excess; ++i) {
0061         if (dims[n_dim_actual - i] != 1) {
0062             squeeze_back = false;
0063             break;
0064         }
0065     }
0066 
0067     return squeeze_back;
0068 }
0069 
0070 
0071 inline std::vector<size_t> squeezeDimensions(const std::vector<size_t>& dims,
0072                                              size_t n_dim_requested) {
0073     auto format_error_message = [&]() -> std::string {
0074         return "Can't interpret dims = " + format_vector(dims) + " as " +
0075                std::to_string(n_dim_requested) + "-dimensional.";
0076     };
0077 
0078     if (n_dim_requested == 0) {
0079         if (!checkDimensions(dims, n_dim_requested)) {
0080             throw std::invalid_argument("Failed dimensions check: " + format_error_message());
0081         }
0082 
0083         return {1ul};
0084     }
0085 
0086     auto n_dim = dims.size();
0087     if (n_dim < n_dim_requested) {
0088         throw std::invalid_argument("Failed 'n_dim < n_dim_requested: " + format_error_message());
0089     }
0090 
0091     if (n_dim_requested == 1ul) {
0092         size_t non_singleton_dim = size_t(-1);
0093         for (size_t i = 0; i < n_dim; ++i) {
0094             if (dims[i] != 1ul) {
0095                 if (non_singleton_dim == size_t(-1)) {
0096                     non_singleton_dim = i;
0097                 } else {
0098                     throw std::invalid_argument("Failed one-dimensional: " +
0099                                                 format_error_message());
0100                 }
0101             }
0102         }
0103 
0104         return {dims[std::min(non_singleton_dim, n_dim - 1)]};
0105     }
0106 
0107     size_t n_dim_excess = dims.size() - n_dim_requested;
0108     for (size_t i = 1; i <= n_dim_excess; ++i) {
0109         if (dims[n_dim - i] != 1) {
0110             throw std::invalid_argument("Failed stripping from back:" + format_error_message());
0111         }
0112     }
0113 
0114     return std::vector<size_t>(dims.begin(),
0115                                dims.end() - static_cast<std::ptrdiff_t>(n_dim_excess));
0116 }
0117 }  // namespace details
0118 
0119 
0120 /*****
0121 inspector<T> {
0122     using type = T
0123     // base_type is the base type inside c++ (e.g. std::vector<int> => int)
0124     using base_type
0125     // hdf5_type is the base read by hdf5 (c-type) (e.g. std::vector<std::string> => const char*)
0126     using hdf5_type
0127 
0128     // Number of dimensions starting from here
0129     static constexpr size_t recursive_ndim
0130     // Is the inner type trivially copyable for optimisation
0131     // If this value is true: data() is mandatory
0132     // If this value is false: getSizeVal, getSize, serialize, unserialize are mandatory
0133     static constexpr bool is_trivially_copyable
0134 
0135     // Reading:
0136     // Allocate the value following dims (should be recursive)
0137     static void prepare(type& val, const std::vector<std::size_t> dims)
0138     // Return the size of the vector pass to/from hdf5 from a vector of dims
0139     static size_t getSize(const std::vector<size_t>& dims)
0140     // Return a pointer of the first value of val (for reading)
0141     static hdf5_type* data(type& val)
0142     // Take a serialized vector 'in', some dims and copy value to val (for reading)
0143     static void unserialize(const hdf5_type* in, const std::vector<size_t>&i, type& val)
0144 
0145 
0146     // Writing:
0147     // Return the size of the vector pass to/from hdf5 from a value
0148     static size_t getSizeVal(const type& val)
0149     // Return a point of the first value of val
0150     static const hdf5_type* data(const type& val)
0151     // Take a val and serialize it inside 'out'
0152     static void serialize(const type& val, hdf5_type* out)
0153     // Return an array of dimensions of the space needed for writing val
0154     static std::vector<size_t> getDimensions(const type& val)
0155 }
0156 *****/
0157 
0158 
0159 namespace details {
0160 template <typename T>
0161 struct type_helper {
0162     using type = unqualified_t<T>;
0163     using base_type = unqualified_t<T>;
0164     using hdf5_type = base_type;
0165 
0166     static constexpr size_t ndim = 0;
0167     static constexpr size_t recursive_ndim = ndim;
0168     static constexpr bool is_trivially_copyable = std::is_trivially_copyable<type>::value;
0169 
0170     static std::vector<size_t> getDimensions(const type& /* val */) {
0171         return {};
0172     }
0173 
0174     static size_t getSizeVal(const type& val) {
0175         return compute_total_size(getDimensions(val));
0176     }
0177 
0178     static size_t getSize(const std::vector<size_t>& dims) {
0179         return compute_total_size(dims);
0180     }
0181 
0182     static void prepare(type& /* val */, const std::vector<size_t>& /* dims */) {}
0183 
0184     static hdf5_type* data(type& val) {
0185         static_assert(is_trivially_copyable, "The type is not trivially copyable");
0186         return &val;
0187     }
0188 
0189     static const hdf5_type* data(const type& val) {
0190         static_assert(is_trivially_copyable, "The type is not trivially copyable");
0191         return &val;
0192     }
0193 
0194     static void serialize(const type& val, hdf5_type* m) {
0195         static_assert(is_trivially_copyable, "The type is not trivially copyable");
0196         *m = val;
0197     }
0198 
0199     static void unserialize(const hdf5_type* vec,
0200                             const std::vector<size_t>& /* dims */,
0201                             type& val) {
0202         static_assert(is_trivially_copyable, "The type is not trivially copyable");
0203         val = vec[0];
0204     }
0205 };
0206 
0207 template <typename T>
0208 struct inspector: type_helper<T> {};
0209 
0210 enum class Boolean : int8_t {
0211     HighFiveFalse = 0,
0212     HighFiveTrue = 1,
0213 };
0214 
0215 template <>
0216 struct inspector<bool>: type_helper<bool> {
0217     using base_type = Boolean;
0218     using hdf5_type = int8_t;
0219 
0220     static constexpr bool is_trivially_copyable = false;
0221 
0222     static hdf5_type* data(type& /* val */) {
0223         throw DataSpaceException("A boolean cannot be read directly.");
0224     }
0225 
0226     static const hdf5_type* data(const type& /* val */) {
0227         throw DataSpaceException("A boolean cannot be written directly.");
0228     }
0229 
0230     static void unserialize(const hdf5_type* vec,
0231                             const std::vector<size_t>& /* dims */,
0232                             type& val) {
0233         val = vec[0] != 0 ? true : false;
0234     }
0235 
0236     static void serialize(const type& val, hdf5_type* m) {
0237         *m = val ? 1 : 0;
0238     }
0239 };
0240 
0241 template <>
0242 struct inspector<std::string>: type_helper<std::string> {
0243     using hdf5_type = const char*;
0244 
0245     static hdf5_type* data(type& /* val */) {
0246         throw DataSpaceException("A std::string cannot be read directly.");
0247     }
0248 
0249     static const hdf5_type* data(const type& /* val */) {
0250         throw DataSpaceException("A std::string cannot be written directly.");
0251     }
0252 
0253     template <class It>
0254     static void serialize(const type& val, It m) {
0255         (*m).assign(val.data(), val.size(), StringPadding::NullTerminated);
0256     }
0257 
0258     template <class It>
0259     static void unserialize(const It& vec, const std::vector<size_t>& /* dims */, type& val) {
0260         const auto& view = *vec;
0261         val.assign(view.data(), view.length());
0262     }
0263 };
0264 
0265 template <>
0266 struct inspector<Reference>: type_helper<Reference> {
0267     using hdf5_type = hobj_ref_t;
0268 
0269     static constexpr bool is_trivially_copyable = false;
0270 
0271     static hdf5_type* data(type& /* val */) {
0272         throw DataSpaceException("A Reference cannot be read directly.");
0273     }
0274 
0275     static const hdf5_type* data(const type& /* val */) {
0276         throw DataSpaceException("A Reference cannot be written directly.");
0277     }
0278 
0279     static void serialize(const type& val, hdf5_type* m) {
0280         hobj_ref_t ref;
0281         val.create_ref(&ref);
0282         *m = ref;
0283     }
0284 
0285     static void unserialize(const hdf5_type* vec,
0286                             const std::vector<size_t>& /* dims */,
0287                             type& val) {
0288         val = type{vec[0]};
0289     }
0290 };
0291 
0292 template <size_t N>
0293 struct inspector<deprecated::FixedLenStringArray<N>> {
0294     using type = deprecated::FixedLenStringArray<N>;
0295     using value_type = char*;
0296     using base_type = deprecated::FixedLenStringArray<N>;
0297     using hdf5_type = char;
0298 
0299     static constexpr size_t ndim = 1;
0300     static constexpr size_t recursive_ndim = ndim;
0301     static constexpr bool is_trivially_copyable = false;
0302 
0303     static std::vector<size_t> getDimensions(const type& val) {
0304         return std::vector<size_t>{val.size()};
0305     }
0306 
0307     static size_t getSizeVal(const type& val) {
0308         return N * compute_total_size(getDimensions(val));
0309     }
0310 
0311     static size_t getSize(const std::vector<size_t>& dims) {
0312         return N * compute_total_size(dims);
0313     }
0314 
0315     static void prepare(type& /* val */, const std::vector<size_t>& dims) {
0316         if (dims[0] > N) {
0317             std::ostringstream os;
0318             os << "Size of FixedlenStringArray (" << N << ") is too small for dims (" << dims[0]
0319                << ").";
0320             throw DataSpaceException(os.str());
0321         }
0322     }
0323 
0324     static hdf5_type* data(type& val) {
0325         return val.data();
0326     }
0327 
0328     static const hdf5_type* data(const type& val) {
0329         return val.data();
0330     }
0331 
0332     static void serialize(const type& val, hdf5_type* m) {
0333         for (size_t i = 0; i < val.size(); ++i) {
0334             std::memcpy(m + i * N, val[i], N);
0335         }
0336     }
0337 
0338     static void unserialize(const hdf5_type* vec, const std::vector<size_t>& dims, type& val) {
0339         for (size_t i = 0; i < dims[0]; ++i) {
0340             std::array<char, N> s;
0341             std::memcpy(s.data(), vec + (i * N), N);
0342             val.push_back(s);
0343         }
0344     }
0345 };
0346 
0347 template <typename T>
0348 struct inspector<std::vector<T>> {
0349     using type = std::vector<T>;
0350     using value_type = unqualified_t<T>;
0351     using base_type = typename inspector<value_type>::base_type;
0352     using hdf5_type = typename inspector<value_type>::hdf5_type;
0353 
0354     static constexpr size_t ndim = 1;
0355     static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
0356     static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
0357                                                   inspector<value_type>::is_trivially_copyable;
0358 
0359     static std::vector<size_t> getDimensions(const type& val) {
0360         std::vector<size_t> sizes(recursive_ndim, 1ul);
0361         sizes[0] = val.size();
0362         if (!val.empty()) {
0363             auto s = inspector<value_type>::getDimensions(val[0]);
0364             assert(s.size() + ndim == sizes.size());
0365             for (size_t i = 0; i < s.size(); ++i) {
0366                 sizes[i + ndim] = s[i];
0367             }
0368         }
0369         return sizes;
0370     }
0371 
0372     static size_t getSizeVal(const type& val) {
0373         return compute_total_size(getDimensions(val));
0374     }
0375 
0376     static size_t getSize(const std::vector<size_t>& dims) {
0377         return compute_total_size(dims);
0378     }
0379 
0380     static void prepare(type& val, const std::vector<size_t>& dims) {
0381         val.resize(dims[0]);
0382         std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
0383         for (auto&& e: val) {
0384             inspector<value_type>::prepare(e, next_dims);
0385         }
0386     }
0387 
0388     static hdf5_type* data(type& val) {
0389         return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
0390     }
0391 
0392     static const hdf5_type* data(const type& val) {
0393         return val.empty() ? nullptr : inspector<value_type>::data(val[0]);
0394     }
0395 
0396     template <class It>
0397     static void serialize(const type& val, It m) {
0398         if (!val.empty()) {
0399             size_t subsize = inspector<value_type>::getSizeVal(val[0]);
0400             for (auto&& e: val) {
0401                 inspector<value_type>::serialize(e, m);
0402                 m += subsize;
0403             }
0404         }
0405     }
0406 
0407     template <class It>
0408     static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
0409         std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
0410         size_t next_size = compute_total_size(next_dims);
0411         for (size_t i = 0; i < dims[0]; ++i) {
0412             inspector<value_type>::unserialize(vec_align + i * next_size, next_dims, val[i]);
0413         }
0414     }
0415 };
0416 
0417 template <>
0418 struct inspector<std::vector<bool>> {
0419     using type = std::vector<bool>;
0420     using value_type = bool;
0421     using base_type = Boolean;
0422     using hdf5_type = uint8_t;
0423 
0424     static constexpr size_t ndim = 1;
0425     static constexpr size_t recursive_ndim = ndim;
0426     static constexpr bool is_trivially_copyable = false;
0427 
0428     static std::vector<size_t> getDimensions(const type& val) {
0429         std::vector<size_t> sizes{val.size()};
0430         return sizes;
0431     }
0432 
0433     static size_t getSizeVal(const type& val) {
0434         return val.size();
0435     }
0436 
0437     static size_t getSize(const std::vector<size_t>& dims) {
0438         if (dims.size() > 1) {
0439             throw DataSpaceException("std::vector<bool> is only 1 dimension.");
0440         }
0441         return dims[0];
0442     }
0443 
0444     static void prepare(type& val, const std::vector<size_t>& dims) {
0445         if (dims.size() > 1) {
0446             throw DataSpaceException("std::vector<bool> is only 1 dimension.");
0447         }
0448         val.resize(dims[0]);
0449     }
0450 
0451     static hdf5_type* data(type& /* val */) {
0452         throw DataSpaceException("A std::vector<bool> cannot be read directly.");
0453     }
0454 
0455     static const hdf5_type* data(const type& /* val */) {
0456         throw DataSpaceException("A std::vector<bool> cannot be written directly.");
0457     }
0458 
0459     static void serialize(const type& val, hdf5_type* m) {
0460         for (size_t i = 0; i < val.size(); ++i) {
0461             m[i] = val[i] ? 1 : 0;
0462         }
0463     }
0464 
0465     static void unserialize(const hdf5_type* vec_align,
0466                             const std::vector<size_t>& dims,
0467                             type& val) {
0468         for (size_t i = 0; i < dims[0]; ++i) {
0469             val[i] = vec_align[i] != 0 ? true : false;
0470         }
0471     }
0472 };
0473 
0474 template <typename T, size_t N>
0475 struct inspector<std::array<T, N>> {
0476     using type = std::array<T, N>;
0477     using value_type = unqualified_t<T>;
0478     using base_type = typename inspector<value_type>::base_type;
0479     using hdf5_type = typename inspector<value_type>::hdf5_type;
0480 
0481     static constexpr size_t ndim = 1;
0482     static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
0483     static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
0484                                                   sizeof(type) == N * sizeof(T) &&
0485                                                   inspector<value_type>::is_trivially_copyable;
0486 
0487     static std::vector<size_t> getDimensions(const type& val) {
0488         std::vector<size_t> sizes{N};
0489         if (!val.empty()) {
0490             auto s = inspector<value_type>::getDimensions(val[0]);
0491             sizes.insert(sizes.end(), s.begin(), s.end());
0492         }
0493         return sizes;
0494     }
0495 
0496     static size_t getSizeVal(const type& val) {
0497         return compute_total_size(getDimensions(val));
0498     }
0499 
0500     static size_t getSize(const std::vector<size_t>& dims) {
0501         return compute_total_size(dims);
0502     }
0503 
0504     static void prepare(type& val, const std::vector<size_t>& dims) {
0505         if (dims[0] > N) {
0506             std::ostringstream os;
0507             os << "Size of std::array (" << N << ") is too small for dims (" << dims[0] << ").";
0508             throw DataSpaceException(os.str());
0509         }
0510 
0511         std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
0512         for (auto&& e: val) {
0513             inspector<value_type>::prepare(e, next_dims);
0514         }
0515     }
0516 
0517     static hdf5_type* data(type& val) {
0518         return inspector<value_type>::data(val[0]);
0519     }
0520 
0521     static const hdf5_type* data(const type& val) {
0522         return inspector<value_type>::data(val[0]);
0523     }
0524 
0525     template <class It>
0526     static void serialize(const type& val, It m) {
0527         size_t subsize = inspector<value_type>::getSizeVal(val[0]);
0528         for (auto& e: val) {
0529             inspector<value_type>::serialize(e, m);
0530             m += subsize;
0531         }
0532     }
0533 
0534     template <class It>
0535     static void unserialize(const It& vec_align, const std::vector<size_t>& dims, type& val) {
0536         if (dims[0] != N) {
0537             std::ostringstream os;
0538             os << "Impossible to pair DataSet with " << dims[0] << " elements into an array with "
0539                << N << " elements.";
0540             throw DataSpaceException(os.str());
0541         }
0542         std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
0543         size_t next_size = compute_total_size(next_dims);
0544         for (size_t i = 0; i < dims[0]; ++i) {
0545             inspector<value_type>::unserialize(vec_align + i * next_size, next_dims, val[i]);
0546         }
0547     }
0548 };
0549 
0550 // Cannot be use for reading
0551 template <typename T>
0552 struct inspector<T*> {
0553     using type = T*;
0554     using value_type = unqualified_t<T>;
0555     using base_type = typename inspector<value_type>::base_type;
0556     using hdf5_type = typename inspector<value_type>::hdf5_type;
0557 
0558     static constexpr size_t ndim = 1;
0559     static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
0560     static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
0561                                                   inspector<value_type>::is_trivially_copyable;
0562 
0563     static size_t getSizeVal(const type& /* val */) {
0564         throw DataSpaceException("Not possible to have size of a T*");
0565     }
0566 
0567     static std::vector<size_t> getDimensions(const type& /* val */) {
0568         throw DataSpaceException("Not possible to have size of a T*");
0569     }
0570 
0571     static const hdf5_type* data(const type& val) {
0572         return reinterpret_cast<const hdf5_type*>(val);
0573     }
0574 
0575     /* it works because there is only T[][][] currently
0576        we will fix it one day */
0577     static void serialize(const type& /* val */, hdf5_type* /* m */) {
0578         throw DataSpaceException("Not possible to serialize a T*");
0579     }
0580 };
0581 
0582 // Cannot be use for reading
0583 template <typename T, size_t N>
0584 struct inspector<T[N]> {
0585     using type = T[N];
0586     using value_type = unqualified_t<T>;
0587     using base_type = typename inspector<value_type>::base_type;
0588     using hdf5_type = typename inspector<value_type>::hdf5_type;
0589 
0590     static constexpr size_t ndim = 1;
0591     static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
0592     static constexpr bool is_trivially_copyable = std::is_trivially_copyable<value_type>::value &&
0593                                                   inspector<value_type>::is_trivially_copyable;
0594 
0595     static void prepare(type& val, const std::vector<size_t>& dims) {
0596         if (dims.size() < 1) {
0597             throw DataSpaceException("Invalid 'dims', must be at least 1 dimensional.");
0598         }
0599 
0600         if (dims[0] != N) {
0601             throw DataSpaceException("Dimensions mismatch.");
0602         }
0603 
0604         std::vector<size_t> next_dims(dims.begin() + 1, dims.end());
0605         for (size_t i = 0; i < dims[0]; ++i) {
0606             inspector<value_type>::prepare(val[i], next_dims);
0607         }
0608     }
0609 
0610     static size_t getSizeVal(const type& val) {
0611         return compute_total_size(getDimensions(val));
0612     }
0613 
0614     static std::vector<size_t> getDimensions(const type& val) {
0615         std::vector<size_t> sizes{N};
0616         if (N > 0) {
0617             auto s = inspector<value_type>::getDimensions(val[0]);
0618             sizes.insert(sizes.end(), s.begin(), s.end());
0619         }
0620         return sizes;
0621     }
0622 
0623     static const hdf5_type* data(const type& val) {
0624         return inspector<value_type>::data(val[0]);
0625     }
0626 
0627     static hdf5_type* data(type& val) {
0628         return inspector<value_type>::data(val[0]);
0629     }
0630 
0631     /* it works because there is only T[][][] currently
0632        we will fix it one day */
0633     static void serialize(const type& val, hdf5_type* m) {
0634         size_t subsize = inspector<value_type>::getSizeVal(val[0]);
0635         for (size_t i = 0; i < N; ++i) {
0636             inspector<value_type>::serialize(val[i], m + i * subsize);
0637         }
0638     }
0639 };
0640 
0641 }  // namespace details
0642 }  // namespace HighFive
0643 
0644 #ifdef H5_USE_BOOST
0645 #include <highfive/boost.hpp>
0646 #endif
0647 
0648 #ifdef H5_USE_EIGEN
0649 #include <highfive/eigen.hpp>
0650 #endif